984 lines
41 KiB
Ruby
984 lines
41 KiB
Ruby
#
|
|
# Copyright (C) 2011 - 2013 Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas 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 Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
|
|
|
describe CalendarEvent do
|
|
|
|
it "should sanitize description" do
|
|
course_model
|
|
@c = CalendarEvent.new
|
|
@c.description = "<a href='#' onclick='alert(12);'>only this should stay</a>"
|
|
@c.context_id = @course.id
|
|
@c.context_type = 'Course'
|
|
@c.save!
|
|
expect(@c.description).to eql("<a href=\"#\">only this should stay</a>")
|
|
end
|
|
|
|
describe "default_values" do
|
|
before(:once) do
|
|
course_model
|
|
@original_start_at = Time.at(1220443500) # 3 Sep 2008 12:05pm (UTC)
|
|
@original_end_at = @original_start_at + 2.hours
|
|
|
|
# Create the initial event
|
|
@event = calendar_event_model(
|
|
:start_at => @original_start_at,
|
|
:end_at => @original_end_at,
|
|
:time_zone_edited => "Mountain Time (US & Canada)"
|
|
)
|
|
end
|
|
|
|
it "should get localized start_at" do
|
|
df = "%Y-%m-%d %H:%M"
|
|
expect(@event.start_at.strftime(df)).to eq "2008-09-03 12:05"
|
|
expect(@event.zoned_start_at.strftime(df)).to eq "2008-09-03 06:05"
|
|
end
|
|
|
|
it "should populate missing dates" do
|
|
event_1 = calendar_event_model
|
|
event_1.start_at = @original_start_at
|
|
event_1.end_at = nil
|
|
event_1.send(:populate_missing_dates)
|
|
expect(event_1.end_at).to eql(event_1.start_at)
|
|
|
|
event_2 = calendar_event_model
|
|
event_2.start_at = nil
|
|
event_2.end_at = @original_end_at
|
|
event_2.send(:populate_missing_dates)
|
|
expect(event_2.start_at).to eql(event_2.end_at)
|
|
|
|
event_3 = calendar_event_model
|
|
event_3.start_at = @original_end_at
|
|
event_3.end_at = @original_start_at
|
|
event_3.send(:populate_missing_dates)
|
|
expect(event_3.end_at).to eql(event_3.start_at)
|
|
end
|
|
|
|
it "should populate all day flag" do
|
|
midnight = Time.at(1361862000) # 2013-02-26 00:00:00
|
|
|
|
event_1 = calendar_event_model(:time_zone_edited => "Mountain Time (US & Canada)")
|
|
event_1.start_at = event_1.end_at = midnight
|
|
event_1.send(:populate_all_day_flag)
|
|
expect(event_1.all_day?).to be_truthy
|
|
expect(event_1.all_day_date.strftime("%Y-%m-%d")).to eq "2013-02-26"
|
|
|
|
event_2 = calendar_event_model(:time_zone_edited => "Mountain Time (US & Canada)")
|
|
event_2.start_at = @original_start_at
|
|
event_2.end_at = @original_end_at
|
|
event_2.send(:populate_all_day_flag)
|
|
expect(event_2.all_day?).to be_falsey
|
|
|
|
event_3 = calendar_event_model(
|
|
:start_at => midnight,
|
|
:end_at => midnight + 1.hour,
|
|
:time_zone_edited => "Mountain Time (US & Canada)"
|
|
)
|
|
event_3.start_at = midnight
|
|
event_3.end_at = midnight + 30.minutes
|
|
event_3.all_day = true
|
|
event_3.send(:populate_all_day_flag)
|
|
expect(event_3.all_day?).to be_truthy
|
|
expect(event_3.end_at).to eql(event_3.start_at)
|
|
end
|
|
|
|
it "should retain all day flag when date is changed (calls :default_values)" do
|
|
# Flag the event as all day
|
|
@event.update_attributes({ :start_at => @original_start_at, :end_at => @original_end_at, :all_day => true })
|
|
expect(@event.all_day?).to be_truthy
|
|
expect(@event.all_day_date.strftime("%Y-%m-%d")).to eq "2008-09-03"
|
|
expect(@event.zoned_start_at.strftime("%H:%M")).to eq "00:00"
|
|
expect(@event.end_at).to eql(@event.zoned_start_at)
|
|
|
|
# Change the date but keep the all day flag as true
|
|
@event.update_attributes({ :start_at => @event.start_at - 1.day, :end_at => @event.end_at - 1.day, :all_day => true })
|
|
expect(@event.all_day?).to be_truthy
|
|
expect(@event.all_day_date.strftime("%Y-%m-%d")).to eq "2008-09-02"
|
|
expect(@event.zoned_start_at.strftime("%H:%M")).to eq "00:00"
|
|
expect(@event.end_at).to eql(@event.zoned_start_at)
|
|
end
|
|
end
|
|
|
|
context "ical" do
|
|
describe "to_ics" do
|
|
it "should not fail for null times" do
|
|
calendar_event_model(:start_at => "", :end_at => "")
|
|
res = @event.to_ics
|
|
expect(res).not_to be_nil
|
|
expect(res.match(/DTSTART/)).to be_nil
|
|
end
|
|
|
|
it "should not return data for null times" do
|
|
calendar_event_model(:start_at => "", :end_at => "")
|
|
res = @event.to_ics(in_own_calendar: false)
|
|
expect(res).to be_nil
|
|
end
|
|
|
|
it "should return string data for events with times" do
|
|
Time.zone = 'UTC'
|
|
calendar_event_model(:start_at => "Sep 3 2008 11:55am", :end_at => "Sep 3 2008 12:00pm")
|
|
# force known value so we can check serialization
|
|
@event.updated_at = Time.at(1220443500) # 3 Sep 2008 12:05pm (UTC)
|
|
res = @event.to_ics
|
|
expect(res).not_to be_nil
|
|
expect(res.match(/DTSTART:20080903T115500Z/)).not_to be_nil
|
|
expect(res.match(/DTEND:20080903T120000Z/)).not_to be_nil
|
|
expect(res.match(/DTSTAMP:20080903T120500Z/)).not_to be_nil
|
|
end
|
|
|
|
it "should return string data for events with times in correct tz" do
|
|
Time.zone = 'Alaska' # -0800
|
|
calendar_event_model(:start_at => "Sep 3 2008 11:55am", :end_at => "Sep 3 2008 12:00pm")
|
|
# force known value so we can check serialization
|
|
@event.updated_at = Time.at(1220472300) # 3 Sep 2008 12:05pm (AKDT)
|
|
res = @event.to_ics
|
|
expect(res).not_to be_nil
|
|
expect(res.match(/DTSTART:20080903T195500Z/)).not_to be_nil
|
|
expect(res.match(/DTEND:20080903T200000Z/)).not_to be_nil
|
|
expect(res.match(/DTSTAMP:20080903T200500Z/)).not_to be_nil
|
|
end
|
|
|
|
it "should return data for events with times" do
|
|
Time.zone = 'UTC'
|
|
calendar_event_model(:start_at => "Sep 3 2008 11:55am", :end_at => "Sep 3 2008 12:00pm")
|
|
# force known value so we can check serialization
|
|
@event.updated_at = Time.at(1220443500) # 3 Sep 2008 12:05pm (UTC)
|
|
res = @event.to_ics(in_own_calendar: false)
|
|
expect(res).not_to be_nil
|
|
expect(res.start.icalendar_tzid).to eq 'UTC'
|
|
expect(res.start.strftime('%Y-%m-%dT%H:%M:%S')).to eq Time.zone.parse("Sep 3 2008 11:55am").in_time_zone('UTC').strftime('%Y-%m-%dT%H:%M:00')
|
|
expect(res.end.icalendar_tzid).to eq 'UTC'
|
|
expect(res.end.strftime('%Y-%m-%dT%H:%M:%S')).to eq Time.zone.parse("Sep 3 2008 12:00pm").in_time_zone('UTC').strftime('%Y-%m-%dT%H:%M:00')
|
|
expect(res.dtstamp.icalendar_tzid).to eq 'UTC'
|
|
expect(res.dtstamp.strftime('%Y-%m-%dT%H:%M:%S')).to eq Time.zone.parse("Sep 3 2008 12:05pm").in_time_zone('UTC').strftime('%Y-%m-%dT%H:%M:00')
|
|
end
|
|
|
|
it "should return data for events with times in correct tz" do
|
|
Time.zone = 'Alaska' # -0800
|
|
calendar_event_model(:start_at => "Sep 3 2008 11:55am", :end_at => "Sep 3 2008 12:00pm")
|
|
# force known value so we can check serialization
|
|
@event.updated_at = Time.at(1220472300) # 3 Sep 2008 12:05pm (AKDT)
|
|
res = @event.to_ics(in_own_calendar: false)
|
|
expect(res).not_to be_nil
|
|
expect(res.start.icalendar_tzid).to eq 'UTC'
|
|
expect(res.start.strftime('%Y-%m-%dT%H:%M:%S')).to eq Time.zone.parse("Sep 3 2008 11:55am").in_time_zone('UTC').strftime('%Y-%m-%dT%H:%M:00')
|
|
expect(res.end.icalendar_tzid).to eq 'UTC'
|
|
expect(res.end.strftime('%Y-%m-%dT%H:%M:%S')).to eq Time.zone.parse("Sep 3 2008 12:00pm").in_time_zone('UTC').strftime('%Y-%m-%dT%H:%M:00')
|
|
expect(res.end.icalendar_tzid).to eq 'UTC'
|
|
expect(res.dtstamp.strftime('%Y-%m-%dT%H:%M:%S')).to eq Time.zone.parse("Sep 3 2008 12:05pm").in_time_zone('UTC').strftime('%Y-%m-%dT%H:%M:00')
|
|
end
|
|
|
|
it "should return string dates for all_day events" do
|
|
calendar_event_model(:start_at => "Sep 3 2008 12:00am")
|
|
expect(@event.all_day).to eql(true)
|
|
expect(@event.end_at).to eql(@event.start_at)
|
|
res = @event.to_ics
|
|
expect(res.match(/DTSTART;VALUE=DATE:20080903/)).not_to be_nil
|
|
expect(res.match(/DTEND;VALUE=DATE:20080903/)).not_to be_nil
|
|
end
|
|
|
|
it "should return a plain-text description" do
|
|
calendar_event_model(:start_at => "Sep 3 2008 12:00am", :description => <<-HTML)
|
|
<p>
|
|
This assignment is due December 16th. <b>Please</b> do the reading.
|
|
<br/>
|
|
<a href="www.example.com">link!</a>
|
|
</p>
|
|
HTML
|
|
ev = @event.to_ics(in_own_calendar: false)
|
|
expect(ev.description).to match_ignoring_whitespace("This assignment is due December 16th. Please do the reading.
|
|
|
|
|
|
[link!](www.example.com)")
|
|
expect(ev.x_alt_desc).to eq @event.description
|
|
end
|
|
|
|
it "should not add verifiers to files unless course or attachment is public" do
|
|
attachment_model(:context => course)
|
|
html = %{<div><a href="/courses/#{@course.id}/files/#{@attachment.id}/download?wrap=1">here</a></div>}
|
|
calendar_event_model(:start_at => "Sep 3 2008 12:00am", :description => html)
|
|
ev = @event.to_ics(in_own_calendar: false)
|
|
expect(ev.description).to_not include("verifier")
|
|
|
|
@attachment.file_state = 'public'
|
|
@attachment.save!
|
|
|
|
AdheresToPolicy::Cache.clear
|
|
ev = @event.to_ics(in_own_calendar: false)
|
|
expect(ev.description).to include("verifier")
|
|
|
|
@attachment.file_state = 'hidden'
|
|
@attachment.save!
|
|
@course.offer
|
|
@course.is_public = true
|
|
@course.save!
|
|
|
|
AdheresToPolicy::Cache.clear
|
|
ev = @event.to_ics(in_own_calendar: false)
|
|
expect(ev.description).to include("verifier")
|
|
end
|
|
|
|
it "should work with media comments in course section events" do
|
|
course_model
|
|
@course.offer
|
|
@course.is_public = true
|
|
|
|
@course.media_objects.create!(:media_id => '0_12345678')
|
|
event = @course.default_section.calendar_events.create!(:start_at => "Sep 3 2008 12:00am",
|
|
:description => %{<p><a id="media_comment_0_12345678" class="instructure_inline_media_comment video_comment" href="/media_objects/0_12345678">media comment</a></p>})
|
|
event.effective_context_code = @course.asset_string
|
|
event.save!
|
|
|
|
ics = event.to_ics
|
|
expect(ics.gsub(/\s+/, '')).to include("/courses/#{@course.id}/media_download?entryId=0_12345678")
|
|
end
|
|
|
|
it "should add a course code to the summary of an event that has a course as an effective_context" do
|
|
course_model
|
|
calendar_event_model(:start_at => "Sep 3 2008 12:00am")
|
|
@event.effective_context_code = @course.asset_string
|
|
ics = @event.to_ics
|
|
expect(ics).to include("SUMMARY:#{@event.title} [#{@course.course_code}]")
|
|
end
|
|
|
|
it "should add a course code to the summary of an event that has a course as its effective_context's context" do
|
|
course_model
|
|
group(:context => @course)
|
|
calendar_event_model(:start_at => "Sep 3 2008 12:00am")
|
|
@event.effective_context_code = @group.asset_string
|
|
ics = @event.to_ics
|
|
expect(ics).to include("SUMMARY:#{@event.title} [#{@course.course_code}]")
|
|
end
|
|
end
|
|
end
|
|
|
|
context "for_user_and_context_codes" do
|
|
before :once do
|
|
course_with_student(:active_all => true)
|
|
@student = @user
|
|
@e1 = @course.calendar_events.create!
|
|
@e2 = @student.calendar_events.create!
|
|
@e3 = Course.create.calendar_events.create!
|
|
end
|
|
|
|
it "should return events explicitly tied to the contexts" do
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [@course.asset_string]).sort_by(&:id)).
|
|
to eql [@e1]
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [@course.asset_string, @student.asset_string]).sort_by(&:id)).
|
|
to eql [@e1, @e2]
|
|
end
|
|
|
|
it "should return events implicitly tied to the contexts (via effective_context_string)" do
|
|
@teacher = user
|
|
@course.enroll_teacher(@teacher).accept!
|
|
course1 = @course
|
|
course_with_teacher(:user => @teacher)
|
|
course2, @course = @course, course1
|
|
g1 = AppointmentGroup.create!(:title => "foo", :contexts => [course1, course2])
|
|
g1.publish!
|
|
ae1 = g1.appointments.create!
|
|
a1 = ae1.reserve_for(@student, @student)
|
|
g2 = AppointmentGroup.create!(:title => "foo", :contexts => [@course], :sub_context_codes => [@course.default_section.asset_string])
|
|
g2.publish!
|
|
ae2 = g2.appointments.create!
|
|
a2 = ae2.reserve_for(@student, @student)
|
|
g3 = AppointmentGroup.create!(:title => "foo", :contexts => [@course])
|
|
g3.publish!
|
|
ae3 = g3.appointments.create!
|
|
pe = @course.calendar_events.create!
|
|
section = @course.default_section
|
|
se = pe.child_events.build
|
|
se.context = section
|
|
se.save!
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [@student.asset_string]).sort_by(&:id)).
|
|
to eql [@e2] # none of the appointments even though they technically are on the user
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [section.asset_string]).sort_by(&:id)).
|
|
to eql [] # none of the appointments even though they technically are on the section
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [@course.asset_string, @student.asset_string]).sort_by(&:id)).
|
|
to eql [@e1, @e2, a1, a2, pe, se]
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [@course.asset_string]).sort_by(&:id)).
|
|
to eql [@e1, a1, a2, pe, se]
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [@course.asset_string]).events_without_child_events.sort_by(&:id)).
|
|
to eql [@e1, a1, a2, se]
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@student, [g1.asset_string, g2.asset_string, g3.asset_string]).sort_by(&:id)).
|
|
to eql [ae1, ae2, ae3]
|
|
|
|
expect(CalendarEvent.for_user_and_context_codes(@teacher, [g1.asset_string, g2.asset_string, g3.asset_string]).events_with_child_events.sort_by(&:id)).
|
|
to eql [ae1, ae2]
|
|
end
|
|
end
|
|
|
|
context "notifications" do
|
|
before :once do
|
|
Notification.create(:name => 'New Event Created', :category => "TestImmediately")
|
|
Notification.create(:name => 'Event Date Changed', :category => "TestImmediately")
|
|
course_with_student(:active_all => true)
|
|
@teacher = user(:active_all => true)
|
|
@course.enroll_teacher(@teacher).accept!
|
|
channel = @student.communication_channels.create(:path => "test_channel_email_#{user.id}", :path_type => "email")
|
|
channel.confirm
|
|
end
|
|
|
|
context "with calendar event created" do
|
|
before :once do
|
|
course_with_student(active_all: true)
|
|
course_with_observer(active_all: true, active_cc: true, associated_user_id: @student.id, course: @course)
|
|
@event1 = @course.calendar_events.build(title: "test")
|
|
@event1.updating_user = @teacher
|
|
@event1.save!
|
|
end
|
|
|
|
context "creation notification" do
|
|
before :once do
|
|
@users = @event1.messages_sent["New Event Created"].map(&:user_id)
|
|
end
|
|
|
|
it "should send to participants", priority: "1", test_id: 186751 do
|
|
expect(@event1.messages_sent).to be_include("New Event Created")
|
|
expect(@users).to include(@student.id)
|
|
end
|
|
|
|
it "should have correct URL" do
|
|
@event1.messages_sent["New Event Created"].each do |message|
|
|
expect(message.url).to include "/courses/#{@course.id}/calendar_events/#{@event1.id}"
|
|
end
|
|
end
|
|
|
|
it "should not send to creating teacher" do
|
|
expect(@users).not_to include(@teacher.id)
|
|
end
|
|
|
|
it "sends to observers" do
|
|
expect(@users).to include(@observer.id)
|
|
end
|
|
end
|
|
|
|
context "with event date edited" do
|
|
before :once do
|
|
@event1.update_attributes(start_at: Time.now, end_at: Time.now)
|
|
end
|
|
|
|
context "edit notification" do
|
|
before :once do
|
|
@users = @event1.messages_sent["Event Date Changed"].map(&:user_id)
|
|
end
|
|
|
|
it "should send to participants", priority: "1", test_id: 193162 do
|
|
expect(@event1.messages_sent).to be_include("Event Date Changed")
|
|
expect(@users).to include(@student.id)
|
|
end
|
|
|
|
it "should have correct url" do
|
|
@event1.messages_sent["Event Date Changed"].each do |message|
|
|
expect(message.url).to include "/courses/#{@course.id}/calendar_events/#{@event1.id}"
|
|
end
|
|
end
|
|
|
|
it "should not send to editing teacher" do
|
|
expect(@users).not_to include(@teacher.id)
|
|
end
|
|
|
|
it "sends to observers" do
|
|
expect(@users).to include(@observer.id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
it "should not send notifications to participants if hidden" do
|
|
course_with_student(:active_all => true)
|
|
event = @course.calendar_events.build(:title => "test", :child_event_data => [{:start_at => "2012-01-01", :end_at => "2012-01-02", :context_code => @course.default_section.asset_string}])
|
|
event.updating_user = @teacher
|
|
event.save!
|
|
expect(event.messages_sent).to be_empty
|
|
|
|
event.update_attribute(:child_event_data, [{:start_at => "2012-01-02", :end_at => "2012-01-03", :context_code => @course.default_section.asset_string}])
|
|
expect(event.messages_sent).to be_empty
|
|
end
|
|
end
|
|
|
|
context "appointments" do
|
|
before :once do
|
|
course_with_student(:active_all => true)
|
|
@student1 = @user
|
|
@other_section = @course.course_sections.create!
|
|
@other_course = Course.create!
|
|
@ag = AppointmentGroup.create(:title => "test", :contexts => [@course])
|
|
@ag.publish!
|
|
@appointment = @ag.appointments.create(:start_at => '2012-01-01 12:00:00', :end_at => '2012-01-01 13:00:00')
|
|
end
|
|
|
|
context "notifications" do
|
|
before do
|
|
Notification.create(:name => 'Appointment Canceled By User', :category => "TestImmediately")
|
|
Notification.create(:name => 'Appointment Deleted For User', :category => "TestImmediately")
|
|
Notification.create(:name => 'Appointment Reserved By User', :category => "TestImmediately")
|
|
Notification.create(:name => 'Appointment Reserved For User', :category => "TestImmediately")
|
|
|
|
@teacher = user(:active_all => true)
|
|
@course.enroll_teacher(@teacher).accept!
|
|
|
|
student_in_course(:course => @course, :active_all => true)
|
|
@student2 = @user
|
|
|
|
c1 = group_category
|
|
@group = c1.groups.create(:context => @course)
|
|
@group.users << @student1 << @student2
|
|
|
|
@ag2 = AppointmentGroup.create!(:title => "test", :contexts => [@course], :sub_context_codes => [c1.asset_string])
|
|
@ag2.publish!
|
|
@appointment2 = @ag2.appointments.create(:start_at => '2012-01-01 12:00:00', :end_at => '2012-01-01 13:00:00')
|
|
|
|
course_with_observer(active_all: true, course: @course, associated_user_id: @student1.id)
|
|
|
|
[@teacher, @student1, @student2, @observer].each do |user|
|
|
channel = user.communication_channels.create(:path => "test_channel_email_#{user.id}", :path_type => "email")
|
|
channel.confirm
|
|
end
|
|
|
|
@expected_users = [@teacher.id, @student1.id, @student2.id, @observer.id].sort
|
|
end
|
|
|
|
def message_recipients_for(notification_name)
|
|
Message.where(notification_id: BroadcastPolicy.notification_finder.by_name(notification_name), user_id: @expected_users).pluck(:user_id).sort
|
|
end
|
|
|
|
it "should notify all participants except the person reserving", priority: "1", test_id: 193149 do
|
|
reservation = @appointment2.reserve_for(@group, @student1)
|
|
expect(message_recipients_for('Appointment Reserved For User')).to eq @expected_users - [@student1.id, @teacher.id]
|
|
end
|
|
|
|
it "should notify all participants except the person canceling the reservation" do
|
|
reservation = @appointment2.reserve_for(@group, @student1)
|
|
reservation.updating_user = @student1
|
|
reservation.destroy
|
|
expect(message_recipients_for('Appointment Deleted For User')).to eq @expected_users - [@student1.id, @teacher.id]
|
|
end
|
|
|
|
it "should notify participants if teacher deletes the appointment time slot", priority: "1", test_id: 193148 do
|
|
reservation = @appointment2.reserve_for(@group, @student1)
|
|
@appointment2.updating_user = @teacher
|
|
@appointment2.destroy
|
|
expect(message_recipients_for('Appointment Deleted For User')).to eq @expected_users - [@teacher.id]
|
|
end
|
|
|
|
it "should notify all participants when the the time slot is canceled", priority: "1", test_id: 502005 do
|
|
reservation = @appointment2.reserve_for(@group, @student1)
|
|
@appointment2.updating_user = @teacher
|
|
user_evt = CalendarEvent.where(context_type: 'Group').first
|
|
user_evt.updating_user = @teacher
|
|
user_evt.destroy
|
|
expect(message_recipients_for('Appointment Deleted For User')).to eq @expected_users - [@teacher.id]
|
|
end
|
|
|
|
it "should notify admins and observers when a user reserves", priority: "1", test_id: 193144 do
|
|
reservation = @appointment.reserve_for(@student1, @student1)
|
|
expect(reservation.messages_sent).to be_include("Appointment Reserved By User")
|
|
expect(reservation.messages_sent["Appointment Reserved By User"].map(&:user_id).sort.uniq).to eql (@course.instructors.map(&:id) + [@observer.id]).sort
|
|
end
|
|
|
|
it "should notify admins and observers when a user reserves a group appointment" do
|
|
reservation = @appointment2.reserve_for(@group, @student1)
|
|
expect(reservation.messages_sent).to be_include("Appointment Reserved By User")
|
|
expect(reservation.messages_sent["Appointment Reserved By User"].map(&:user_id).sort.uniq).to eql (@course.instructors.map(&:id) + [@observer.id]).sort
|
|
end
|
|
|
|
it "should notify admins and observers when a user cancels", priority: "1", test_id: 193147 do
|
|
reservation = @appointment.reserve_for(@student1, @student1)
|
|
reservation.updating_user = @student1
|
|
reservation.destroy
|
|
expect(reservation.messages_sent).to be_include("Appointment Canceled By User")
|
|
expect(reservation.messages_sent["Appointment Canceled By User"].map(&:user_id).sort.uniq).to eql (@course.instructors.map(&:id) + [@observer.id]).sort
|
|
end
|
|
end
|
|
|
|
it "should allow multiple participants in an appointment, up to the limit" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :participants_per_appointment => 2,
|
|
:new_appointments => [['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
ag.publish!
|
|
appointment = ag.appointments.first
|
|
|
|
student_in_course(:course => @course, :active_all => true)
|
|
@other_student = @user
|
|
student_in_course(:course => @course, :active_all => true)
|
|
@unlucky_student = @user
|
|
|
|
expect(appointment.reserve_for(@student1, @student1)).not_to be_nil
|
|
expect(appointment.reserve_for(@other_student, @other_student)).not_to be_nil
|
|
expect { appointment.reserve_for(@unlucky_student, @unlucky_student) }.to raise_error
|
|
end
|
|
|
|
it "should give preference to the calendar's appointment limit" do
|
|
ag = AppointmentGroup.create!(
|
|
:title => "testing...",
|
|
:contexts => [@course],
|
|
:participants_per_appointment => 2,
|
|
:new_appointments => [['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
ag.publish!
|
|
appointment = ag.appointments.first
|
|
appointment.participants_per_appointment = 3
|
|
appointment.save!
|
|
|
|
s1, s2, s3 = 3.times.map {
|
|
student_in_course(:course => @course, :active_all => true)
|
|
@user
|
|
}
|
|
|
|
expect(appointment.reserve_for(@student1, @student1)).not_to be_nil
|
|
expect(appointment.reserve_for(s1, s1)).not_to be_nil
|
|
expect(appointment.reserve_for(s2, s2)).not_to be_nil
|
|
expect { expect(appointment.reserve_for(s3, s3)).not_to be_nil }.to raise_error
|
|
|
|
# should be able to unset the participant limit too
|
|
appointment.participants_per_appointment = nil
|
|
appointment.save!
|
|
expect(appointment.reserve_for(s3, s3)).not_to be_nil
|
|
end
|
|
|
|
it "should revert to the appointment group's participant_limit when appropriate" do
|
|
ag = AppointmentGroup.create!(
|
|
:title => "testing...",
|
|
:contexts => [@course],
|
|
:participants_per_appointment => 2,
|
|
:new_appointments => [['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
ag.publish!
|
|
|
|
appointment = ag.appointments.first
|
|
appointment.participants_per_appointment = 3
|
|
appointment.save!
|
|
expect(appointment.participants_per_appointment).to eql 3
|
|
|
|
appointment.participants_per_appointment = 2
|
|
appointment.save!
|
|
expect(appointment.read_attribute(:participants_per_limit)).to be_nil
|
|
expect(appointment.override_participants_per_appointment?).to be_falsey
|
|
expect(appointment.participants_per_appointment).to eql 2
|
|
end
|
|
|
|
it "should not let participants exceed max_appointments_per_participant" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :max_appointments_per_participant => 1,
|
|
:new_appointments => [['2012-01-01 12:00:00', '2012-01-01 13:00:00'], ['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
ag.publish!
|
|
appointment = ag.appointments.first
|
|
appointment2 = ag.appointments.last
|
|
|
|
appointment.reserve_for(@student1, @student1)
|
|
expect { appointment2.reserve_for(@student1, @student1) }.to raise_error
|
|
end
|
|
|
|
it "should cancel existing reservations if cancel_existing = true" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :max_appointments_per_participant => 1,
|
|
:new_appointments => [['2012-01-01 12:00:00', '2012-01-01 13:00:00'], ['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
ag.publish!
|
|
appointment = ag.appointments.first
|
|
appointment2 = ag.appointments.last
|
|
|
|
r1 = appointment.reserve_for(@student1, @student1)
|
|
expect { appointment2.reserve_for(@student1, @student1, :cancel_existing => true) }.not_to raise_error
|
|
expect(r1.reload).to be_deleted
|
|
end
|
|
|
|
it "should save comments with appointment" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course],
|
|
:max_appointments_per_participant => 1,
|
|
:new_appointments => [['2012-01-01 12:00:00',
|
|
'2012-01-01 13:00:00'],
|
|
['2012-01-01 13:00:00',
|
|
'2012-01-01 14:00:00']
|
|
]
|
|
)
|
|
ag.publish!
|
|
appointment = ag.appointments.first
|
|
r1 = appointment.reserve_for(@student1, @student1, :comments => "my appointment notes")
|
|
r1.reload
|
|
expect(r1.comments).to eq("my appointment notes")
|
|
end
|
|
|
|
it "should enforce the section" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course.course_sections.create],
|
|
:new_appointments => [['2012-01-01 12:00:00', '2012-01-01 13:00:00']]
|
|
)
|
|
ag.publish!
|
|
appointment = ag.appointments.first
|
|
|
|
expect { appointment.reserve_for(@student1, @student1) }.to raise_error
|
|
end
|
|
|
|
it "should enforce the group category" do
|
|
teacher = user(:active_all => true)
|
|
@course.enroll_teacher(teacher).accept!
|
|
c1 = group_category
|
|
g1 = c1.groups.create(:context => @course)
|
|
c2 = group_category(name: "bar")
|
|
g2 = c2.groups.create(:context => @course)
|
|
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :sub_context_codes => [c1.asset_string],
|
|
:new_appointments => [['2012-01-01 12:00:00', '2012-01-01 13:00:00']]
|
|
)
|
|
appointment = ag.appointments.first
|
|
ag.publish!
|
|
|
|
expect { appointment.reserve_for(g2, teacher) }.to raise_error
|
|
expect { appointment.reserve_for(g1, teacher) }.not_to raise_error
|
|
end
|
|
|
|
it "should only accept users with StudentEnrollments as valid user participants" do
|
|
expect(@ag.eligible_participant?(@student1)).to be_truthy
|
|
expect { @appointment.reserve_for(@student1, @student1) }.not_to raise_error
|
|
|
|
# both a student and a teacher
|
|
student_in_course(:course => @course, :active_all => true)
|
|
@user.teacher_enrollments.create!(:course => @course)
|
|
expect(@ag.eligible_participant?(@user)).to be_truthy
|
|
expect { @appointment.reserve_for(@user, @user) }.not_to raise_error
|
|
|
|
# just a teacher
|
|
user(:active_all => true)
|
|
@course.enroll_teacher(@user).accept!
|
|
expect(@ag.eligible_participant?(@user)).to be_falsey
|
|
expect { @appointment.reserve_for(@user, @user) }.to raise_error
|
|
end
|
|
|
|
it "should lock the appointment once it is reserved" do
|
|
expect(@appointment).to be_active
|
|
expect(@appointment.reserve_for(@student1, @student1)).not_to be_nil
|
|
expect(@appointment).to be_locked
|
|
end
|
|
|
|
it "should unlock the appointment when the last reservation is canceled" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :participants_per_appointment => 2,
|
|
:new_appointments => [['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
appointment = ag.appointments.first
|
|
student_in_course(:course => @course, :active_all => true)
|
|
@other_student = @user
|
|
|
|
expect(appointment).to be_active
|
|
r1 = appointment.reserve_for(@student1, @student1).reload
|
|
expect(appointment).to be_locked
|
|
r2 = appointment.reserve_for(@other_student, @other_student).reload
|
|
r2.destroy
|
|
expect(appointment.reload).to be_locked
|
|
r1.destroy
|
|
expect(appointment.reload).to be_active
|
|
end
|
|
|
|
it "should copy the group attributes to the initial appointments" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :description => "hello\nworld",
|
|
:new_appointments => [['2012-01-01 12:00:00', '2012-01-01 13:00:00']]
|
|
)
|
|
e = ag.appointments.first
|
|
expect(e.title).to eql 'test'
|
|
expect(e.description).to eql "hello<br/>\r\nworld"
|
|
end
|
|
|
|
it "should copy changed group attributes to existing appointments" do
|
|
@ag.update_attributes(:title => 'changed!', :description => "test\n123")
|
|
e = @ag.appointments.first.reload
|
|
expect(e.title).to eql 'changed!'
|
|
expect(e.description).to eql "test<br/>\r\n123"
|
|
end
|
|
|
|
it "should not copy group description if appointment is overridden" do
|
|
@appointment.description = "pizza party"
|
|
@appointment.save!
|
|
|
|
@ag.description = "boring meeting"
|
|
@ag.save!
|
|
|
|
expect(@appointment.description).to eq "pizza party"
|
|
expect(@ag.description).to eq "boring meeting"
|
|
end
|
|
|
|
it "should copy the group attributes to subsequent appointments" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course])
|
|
ag.update_attributes(
|
|
:title => 'haha',
|
|
:new_appointments => [['2012-01-01 12:00:00', '2012-01-01 13:00:00']]
|
|
)
|
|
e = ag.appointments.first
|
|
expect(e.title).to eql 'haha'
|
|
end
|
|
|
|
it "should ignore changes to locked attributes on the appointment" do
|
|
@appointment.update_attributes(:start_at => '2012-01-01 12:30:00', :title => 'you wish')
|
|
expect(@appointment.title).to eql 'test'
|
|
expect(@appointment.start_at).to eql Time.parse('2012-01-01 12:30:00Z')
|
|
end
|
|
|
|
it "should allow a user to re-reserve a slot after canceling" do
|
|
ag = AppointmentGroup.create(:title => "test", :contexts => [@course], :participants_per_appointment => 1,
|
|
:new_appointments => [['2012-01-01 13:00:00', '2012-01-01 14:00:00']]
|
|
)
|
|
appointment = ag.appointments.first
|
|
|
|
r1 = appointment.reserve_for(@student1, @student1).reload
|
|
expect(ag.reload.available_slots).to eql 0
|
|
r1.destroy
|
|
expect(ag.reload.available_slots).to eql 1
|
|
expect { appointment.reserve_for(@student1, @student1) }.not_to raise_error
|
|
expect(ag.reload.available_slots).to eql 0
|
|
end
|
|
|
|
it "should always allow editing the description on an appointment" do
|
|
@appointment.update_attribute :workflow_state, "locked"
|
|
@appointment.description = "bacon"
|
|
@appointment.save!
|
|
expect(@appointment.description).to eq "bacon"
|
|
end
|
|
end
|
|
|
|
context "child_events" do
|
|
it "should delete child events when deleting the parent" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.save!
|
|
|
|
@event.destroy
|
|
|
|
expect(@event.reload).to be_deleted
|
|
expect(child.reload).to be_deleted
|
|
end
|
|
|
|
context "bulk updating" do
|
|
before :once do
|
|
course_with_teacher
|
|
end
|
|
|
|
it "should validate child events" do
|
|
expect {
|
|
@course.calendar_events.create! :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string}
|
|
]
|
|
}.to raise_error(/Can't update child events unless an updating_user is set/)
|
|
|
|
expect {
|
|
event = @course.calendar_events.build :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => "invalid_1"}
|
|
]
|
|
event.updating_user = @user
|
|
event.save!
|
|
}.to raise_error(/Invalid child event context/)
|
|
|
|
expect {
|
|
other_section = Course.create!.default_section
|
|
event = @course.calendar_events.build :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => other_section.asset_string}
|
|
]
|
|
event.updating_user = @user
|
|
event.save!
|
|
}.to raise_error(/Invalid child event context/)
|
|
|
|
expect {
|
|
event = @course.calendar_events.build :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-01 13:00:00", :end_at => "2012-01-01 14:00:00", :context_code => @course.default_section.asset_string}
|
|
]
|
|
event.updating_user = @user
|
|
event.save!
|
|
}.to raise_error(/Duplicate child event contexts/)
|
|
end
|
|
|
|
it "should create child events" do
|
|
s2 = @course.course_sections.create!
|
|
e1 = @course.calendar_events.build :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-02 12:00:00", :end_at => "2012-01-02 13:00:00", :context_code => s2.asset_string},
|
|
]
|
|
e1.updating_user = @user
|
|
e1.save!
|
|
|
|
e1.reload
|
|
events = e1.child_events.sort_by(&:id)
|
|
expect(events.map(&:context_code)).to eql [@course.default_section.asset_string, s2.asset_string]
|
|
expect(events.map(&:effective_context_code).uniq).to eql [@course.asset_string]
|
|
expect(e1.start_at).to eql events.first.start_at
|
|
expect(e1.end_at).to eql events.last.end_at
|
|
end
|
|
|
|
it "should update child events" do
|
|
s2 = @course.course_sections.create!
|
|
s3 = @course.course_sections.create!
|
|
e1 = @course.calendar_events.build :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-02 12:00:00", :end_at => "2012-01-02 13:00:00", :context_code => s2.asset_string},
|
|
]
|
|
e1.updating_user = @user
|
|
e1.save!
|
|
e1.reload
|
|
events1 = e1.child_events.sort_by(&:id)
|
|
|
|
e1.update_attributes :child_event_data => [
|
|
{:start_at => "2012-01-01 13:00:00", :end_at => "2012-01-01 14:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-02 12:00:00", :end_at => "2012-01-02 13:00:00", :context_code => s3.asset_string},
|
|
]
|
|
e1.reload
|
|
events2 = e1.child_events.sort_by(&:id)
|
|
expect(events2.size).to eql 2
|
|
|
|
expect(events1.first.reload).to eql events2.first
|
|
expect(events1.last.reload).to be_deleted
|
|
end
|
|
|
|
it "should not try to migrate resources to section context" do
|
|
attachment_with_context(@course)
|
|
s2 = @course.course_sections.create!
|
|
e1 = @course.calendar_events.build :title => "ohai",
|
|
:description => "<img src='/courses/#{@course.id}/files/#{@attachment.id}/preview'>",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-02 12:00:00", :end_at => "2012-01-02 13:00:00", :context_code => s2.asset_string},
|
|
]
|
|
e1.updating_user = @user
|
|
e1.save!
|
|
e1.child_event_data = [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-02 22:00:00", :end_at => "2012-01-02 23:00:00", :context_code => s2.asset_string},
|
|
]
|
|
expect { e1.save! }.not_to raise_error
|
|
end
|
|
|
|
it "should delete all child events" do
|
|
s2 = @course.course_sections.create!
|
|
s3 = @course.course_sections.create!
|
|
e1 = @course.calendar_events.build :title => "ohai",
|
|
:child_event_data => [
|
|
{:start_at => "2012-01-01 12:00:00", :end_at => "2012-01-01 13:00:00", :context_code => @course.default_section.asset_string},
|
|
{:start_at => "2012-01-02 12:00:00", :end_at => "2012-01-02 13:00:00", :context_code => s2.asset_string},
|
|
]
|
|
e1.updating_user = @user
|
|
e1.save!
|
|
e1.reload
|
|
e1.update_attributes :remove_child_events => true
|
|
expect(e1.child_events.reload).to be_empty
|
|
end
|
|
end
|
|
|
|
context "cascading" do
|
|
it "should copy cascaded attributes when creating a child event" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.save!
|
|
expect(child.start_at).to be_nil
|
|
expect(child.title).to eql @event.title
|
|
end
|
|
|
|
it "should update cascaded attributes on the child events whenever the parent is updated" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.save!
|
|
child.reload
|
|
orig_start_at = child.start_at
|
|
|
|
@event.title = 'asdf'
|
|
@event.start_at = Time.now.utc
|
|
@event.save!
|
|
expect(child.reload.title).to eql 'asdf'
|
|
expect(child.start_at).to eql orig_start_at
|
|
end
|
|
|
|
it "should disregard attempted changes to cascaded attributes" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.save!
|
|
child.reload
|
|
orig_start_at = child.start_at
|
|
|
|
child.title = 'asdf'
|
|
child.start_at = Time.now.utc
|
|
child.save!
|
|
expect(child.title).to eql 'some event'
|
|
expect(child.start_at).not_to eql orig_start_at
|
|
end
|
|
end
|
|
|
|
context "locking" do
|
|
it "should copy all attributes when creating a locked child event" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.workflow_state = :locked
|
|
child.save!
|
|
expect(child.start_at).to eql @event.start_at
|
|
expect(child.title).to eql @event.title
|
|
end
|
|
|
|
it "should update locked child events whenever the parent is updated" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.workflow_state = :locked
|
|
child.save!
|
|
|
|
@event.title = 'asdf'
|
|
@event.save!
|
|
expect(child.reload.title).to eql 'asdf'
|
|
end
|
|
|
|
it "should disregard attempted changes to locked attributes" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.workflow_state = :locked
|
|
child.save!
|
|
|
|
child.title = 'asdf'
|
|
child.save!
|
|
expect(child.title).to eql 'some event'
|
|
end
|
|
|
|
it "should unlock events when the last child is deleted" do
|
|
calendar_event_model(:start_at => "Sep 3 2008", :title => "some event")
|
|
@event.workflow_state = :locked
|
|
@event.save!
|
|
child = @event.child_events.build
|
|
child.context = user
|
|
child.workflow_state = :locked
|
|
child.save!
|
|
|
|
child.destroy
|
|
expect(@event.reload).to be_active
|
|
expect(child.reload).to be_deleted
|
|
end
|
|
end
|
|
end
|
|
end
|