963 lines
38 KiB
Ruby
963 lines
38 KiB
Ruby
#
|
|
# Copyright (C) 2011 - 2012 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 Quiz do
|
|
before(:each) do
|
|
course
|
|
end
|
|
|
|
describe ".mark_quiz_edited" do
|
|
it "should mark a quiz as having unpublished changes" do
|
|
quiz = @course.quizzes.create! :title => "hello"
|
|
quiz.published_at = Time.now
|
|
quiz.publish!
|
|
quiz.unpublished_changes?.should be_false
|
|
|
|
Quiz.mark_quiz_edited(quiz.id)
|
|
quiz.reload.unpublished_changes?.should be_true
|
|
end
|
|
end
|
|
|
|
describe "#publish!" do
|
|
it "sets the workflow state to available and save!s the quiz" do
|
|
quiz = Quiz.new(:title => "hello")
|
|
quiz.expects(:save!).once
|
|
quiz.publish!
|
|
quiz.workflow_state.should == 'available'
|
|
end
|
|
end
|
|
|
|
it "should infer the times if none given" do
|
|
q = factory_with_protected_attributes(@course.quizzes,
|
|
:title => "new quiz",
|
|
:due_at => "Sep 3 2008 12:00am",
|
|
:lock_at => "Sep 3 2008 12:00am",
|
|
:unlock_at => "Sep 3 2008 12:00am",
|
|
:quiz_type => 'assignment',
|
|
:workflow_state => 'available')
|
|
due_at = q.due_at
|
|
q.due_at.should == Time.parse("Sep 3 2008 12:00am UTC")
|
|
lock_at = q.lock_at
|
|
unlock_at = q.unlock_at
|
|
q.lock_at.should == Time.parse("Sep 3 2008 12:00am UTC")
|
|
q.assignment.due_at.should == Time.parse("Sep 3 2008 12:00am UTC")
|
|
q.infer_times
|
|
q.save!
|
|
q.due_at.should == due_at.end_of_day
|
|
q.assignment.due_at.should == due_at.end_of_day
|
|
q.lock_at.should == lock_at.end_of_day
|
|
q.assignment.lock_at.should == lock_at.end_of_day
|
|
# Unlock at should not be fudged so teacher's can say this assignment
|
|
# is available at 12 am.
|
|
q.unlock_at.should == unlock_at.midnight
|
|
q.assignment.unlock_at.should == unlock_at.midnight
|
|
end
|
|
|
|
it "should set the due time to 11:59pm if only given a date" do
|
|
params = { :quiz => {
|
|
:title => "Test Quiz",
|
|
:due_at => Time.zone.today.to_s,
|
|
:lock_at => Time.zone.today.to_s,
|
|
:unlock_at => Time.zone.today.to_s
|
|
}
|
|
}
|
|
q = @course.quizzes.create!(params[:quiz])
|
|
q.infer_times
|
|
q.due_at.should be_an_instance_of ActiveSupport::TimeWithZone
|
|
q.due_at.time_zone.should == Time.zone
|
|
q.due_at.hour.should eql 23
|
|
q.due_at.min.should eql 59
|
|
q.lock_at.time_zone.should == Time.zone
|
|
q.lock_at.hour.should eql 23
|
|
q.lock_at.min.should eql 59
|
|
# Unlock at should not be fudged so teacher's can say this assignment
|
|
# is available at 12 am.
|
|
q.unlock_at.time_zone.should == Time.zone
|
|
q.unlock_at.hour.should eql 0
|
|
q.unlock_at.min.should eql 0
|
|
end
|
|
|
|
it "should not set the due time to 11:59pm if passed a time of midnight" do
|
|
params = { :quiz => { :title => "Test Quiz", :due_at => "Jan 1 2011 12:00am" } }
|
|
q = @course.quizzes.create!(params[:quiz])
|
|
q.due_at.hour.should eql 0
|
|
q.due_at.min.should eql 0
|
|
end
|
|
|
|
it "should convert a date object to a time and set the time to 11:59pm" do
|
|
Time.zone = 'Alaska'
|
|
params = { :quiz => { :title => 'Test Quiz', :due_at => Time.zone.today } }
|
|
quiz = @course.quizzes.create!(params[:quiz])
|
|
quiz.due_at.should be_an_instance_of ActiveSupport::TimeWithZone
|
|
quiz.due_at.zone.should eql Time.zone.now.dst? ? 'AKDT' : 'AKST'
|
|
quiz.due_at.hour.should eql 23
|
|
quiz.due_at.min.should eql 59
|
|
end
|
|
|
|
it "should set the due date time correctly" do
|
|
time_string = "Dec 30, 2011 12:00 pm"
|
|
expected = "2011-12-30 19:00:00 #{Time.now.utc.strftime("%Z")}"
|
|
Time.zone = "Mountain Time (US & Canada)"
|
|
quiz = @course.quizzes.create(:title => "sad quiz", :due_at => time_string, :lock_at => time_string, :unlock_at => time_string)
|
|
quiz.due_at.utc.strftime("%Y-%m-%d %H:%M:%S %Z").should == expected
|
|
quiz.lock_at.utc.strftime("%Y-%m-%d %H:%M:%S %Z").should == expected
|
|
quiz.unlock_at.utc.strftime("%Y-%m-%d %H:%M:%S %Z").should == expected
|
|
Time.zone = nil
|
|
end
|
|
|
|
it "should initialize with default settings" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
q.shuffle_answers.should eql(false)
|
|
q.show_correct_answers.should eql(true)
|
|
q.allowed_attempts.should eql(1)
|
|
q.scoring_policy.should eql('keep_highest')
|
|
end
|
|
|
|
it "should update the assignment it is associated with" do
|
|
a = @course.assignments.create!(:title => "some assignment", :points_possible => 5)
|
|
a.points_possible.should eql(5.0)
|
|
a.submission_types.should_not eql("online_quiz")
|
|
q = @course.quizzes.build(:assignment_id => a.id, :title => "some quiz", :points_possible => 10)
|
|
q.workflow_state = 'available'
|
|
q.save
|
|
q.should be_available
|
|
q.assignment_id.should eql(a.id)
|
|
q.assignment.should eql(a)
|
|
a.reload
|
|
a.quiz.should eql(q)
|
|
q.points_possible.should eql(10.0)
|
|
q.assignment.submission_types.should eql("online_quiz")
|
|
q.assignment.points_possible.should eql(10.0)
|
|
|
|
g = @course.assignment_groups.create!(:name => "new group")
|
|
q.assignment_group_id = g.id
|
|
q.save
|
|
q.reload
|
|
a.reload
|
|
a.assignment_group.should eql(g)
|
|
q.assignment_group_id.should eql(g.id)
|
|
|
|
g2 = @course.assignment_groups.create!(:name => "new group2")
|
|
a.assignment_group = g2
|
|
a.save
|
|
a.reload
|
|
q.reload
|
|
q.assignment_group_id.should eql(g2.id)
|
|
a.assignment_group.should eql(g2)
|
|
end
|
|
|
|
it "shouldn't create a new assignment on every edit" do
|
|
a_count = Assignment.count
|
|
a = @course.assignments.create!(:title => "some assignment", :points_possible => 5)
|
|
a.points_possible.should eql(5.0)
|
|
a.submission_types.should_not eql("online_quiz")
|
|
q = @course.quizzes.build(:title => "some quiz", :points_possible => 10)
|
|
q.workflow_state = 'available'
|
|
q.assignment_id = a.id
|
|
q.save
|
|
q.quiz_type = 'assignment'
|
|
q.save
|
|
q.should be_available
|
|
q.assignment_id.should eql(a.id)
|
|
q.assignment.should eql(a)
|
|
a.reload
|
|
a.quiz.should eql(q)
|
|
q.points_possible.should eql(10.0)
|
|
a.submission_types.should eql("online_quiz")
|
|
a.points_possible.should eql(10.0)
|
|
Assignment.count.should eql(a_count + 1)
|
|
end
|
|
|
|
it "should not send a message if notify_of_update is blank" do
|
|
Notification.create!(:name => 'Assignment Changed')
|
|
@course.offer
|
|
a = @course.assignments.create!(:title => "some assignment", :points_possible => 5)
|
|
a.points_possible.should eql(5.0)
|
|
a.submission_types.should_not eql("online_quiz")
|
|
a.update_attribute(:created_at, Time.now - (40 * 60))
|
|
q = @course.quizzes.build(:assignment_id => a.id, :title => "some quiz", :points_possible => 10)
|
|
q.workflow_state = 'available'
|
|
q.assignment.expects(:save_without_broadcasting!).at_least_once
|
|
q.save
|
|
q.assignment.messages_sent.should be_empty
|
|
end
|
|
|
|
it "should send a message if notify_of_update is set" do
|
|
Notification.create!(:name => 'Assignment Changed')
|
|
@course.offer
|
|
a = @course.assignments.create!(:title => "some assignment", :points_possible => 5)
|
|
a.points_possible.should eql(5.0)
|
|
a.submission_types.should_not eql("online_quiz")
|
|
a.update_attribute(:created_at, Time.now - (40 * 60))
|
|
q = @course.quizzes.build(:assignment_id => a.id, :title => "some quiz", :points_possible => 10)
|
|
q.workflow_state = 'available'
|
|
q.notify_of_update = 1
|
|
q.assignment.expects(:save_without_broadcasting!).never
|
|
q.save
|
|
q.assignment.messages_sent.should include('Assignment Changed')
|
|
end
|
|
|
|
it "should delete the assignment if the quiz is no longer graded" do
|
|
a = @course.assignments.create!(:title => "some assignment", :points_possible => 5)
|
|
a.points_possible.should eql(5.0)
|
|
a.submission_types.should_not eql("online_quiz")
|
|
q = @course.quizzes.build(:assignment_id => a.id, :title => "some quiz", :points_possible => 10)
|
|
q.workflow_state = 'available'
|
|
q.save
|
|
q.should be_available
|
|
q.assignment_id.should eql(a.id)
|
|
q.assignment.should eql(a)
|
|
a.reload
|
|
a.quiz.should eql(q)
|
|
q.points_possible.should eql(10.0)
|
|
q.assignment.submission_types.should eql("online_quiz")
|
|
q.assignment.points_possible.should eql(10.0)
|
|
q.quiz_type = "practice_quiz"
|
|
q.save
|
|
q.assignment_id.should eql(nil)
|
|
end
|
|
|
|
it "should not create an assignment for ungraded quizzes" do
|
|
g = @course.assignment_groups.create!(:name => "new group")
|
|
q = @course.quizzes.build(:title => "some quiz", :quiz_type => "survey", :assignment_group_id => g.id)
|
|
q.workflow_state = 'available'
|
|
q.save!
|
|
q.should be_available
|
|
q.assignment_id.should be_nil
|
|
end
|
|
|
|
it "should not create the assignment if unpublished" do
|
|
g = @course.assignment_groups.create!(:name => "new group")
|
|
q = @course.quizzes.build(:title => "some quiz", :quiz_type => "assignment", :assignment_group_id => g.id)
|
|
q.save!
|
|
q.should_not be_available
|
|
q.assignment_id.should be_nil
|
|
q.assignment_group_id.should eql(g.id)
|
|
end
|
|
|
|
it "should create the assignment if created in published state" do
|
|
g = @course.assignment_groups.create!(:name => "new group")
|
|
q = @course.quizzes.build(:title => "some quiz", :quiz_type => "assignment", :assignment_group_id => g.id)
|
|
q.workflow_state = 'available'
|
|
q.save!
|
|
q.should be_available
|
|
q.assignment_id.should_not be_nil
|
|
q.assignment_group_id.should eql(g.id)
|
|
q.assignment.assignment_group_id.should eql(g.id)
|
|
end
|
|
|
|
it "should create the assignment if published after being created" do
|
|
g = @course.assignment_groups.create!(:name => "new group")
|
|
q = @course.quizzes.build(:title => "some quiz", :quiz_type => "assignment", :assignment_group_id => g.id)
|
|
q.save!
|
|
q.should_not be_available
|
|
q.assignment_id.should be_nil
|
|
q.assignment_group_id.should eql(g.id)
|
|
q.workflow_state = 'available'
|
|
q.save!
|
|
q.should be_available
|
|
q.assignment_id.should_not be_nil
|
|
q.assignment_group_id.should eql(g.id)
|
|
q.assignment.assignment_group_id.should eql(g.id)
|
|
end
|
|
|
|
it "should return a zero question count but valid unpublished question count until the quiz is generated" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 1)
|
|
q.quiz_questions.create!(:quiz_group => g)
|
|
q.quiz_questions.create!(:quiz_group => g)
|
|
q.quiz_questions.create!()
|
|
q.quiz_questions.create!()
|
|
# this is necessary because of some caching that happens on the quiz object, that is not a factor in production
|
|
q.root_entries(true)
|
|
q.save
|
|
q.question_count.should eql(0)
|
|
q.unpublished_question_count.should eql(3)
|
|
end
|
|
|
|
it "should return processed root entries for each question/group" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 1, :question_points => 2)
|
|
|
|
qq1 = q.quiz_questions.create!(:question_data => { :name => "test 1" }, :quiz_group => g)
|
|
# make sure we handle sorting with nil positions
|
|
QuizQuestion.where(:id => qq1).update_all(:position => nil)
|
|
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2" }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3" })
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4" })
|
|
q.save
|
|
q.quiz_questions.length.should eql(4)
|
|
q.quiz_groups.length.should eql(1)
|
|
g.quiz_questions(true).length.should eql(2)
|
|
|
|
entries = q.root_entries(true)
|
|
entries.length.should eql(3)
|
|
entries[0][:questions].should_not be_nil
|
|
entries[1][:answers].should_not be_nil
|
|
entries[2][:answers].should_not be_nil
|
|
end
|
|
|
|
it "should generate valid quiz data" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 1, :question_points => 2)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1" }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2" }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3" })
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4" })
|
|
q.quiz_data.should be_nil
|
|
q.generate_quiz_data
|
|
q.save
|
|
q.quiz_data.should_not be_nil
|
|
data = q.quiz_data rescue nil
|
|
data.should_not be_nil
|
|
end
|
|
|
|
it "should return quiz data once the quiz is generated" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 1, :question_points => 2)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3", })
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4", })
|
|
q.quiz_data.should be_nil
|
|
q.generate_quiz_data
|
|
q.save
|
|
|
|
data = q.stored_questions
|
|
data.length.should eql(3)
|
|
data[0][:questions].should_not be_nil
|
|
data[1][:answers].should_not be_nil
|
|
data[2][:answers].should_not be_nil
|
|
end
|
|
|
|
it "should shuffle answers for the questions" do
|
|
q = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true)
|
|
q.quiz_questions.create!(:question_data => {:name => 'test 3', 'question_type' => 'multiple_choice_question', 'answers' => {'answer_0' => {'answer_text' => '1'}, 'answer_1' => {'answer_text' => '2'}, 'answer_2' => {'answer_text' => '3'},'answer_3' => {'answer_text' => '4'},'answer_4' => {'answer_text' => '5'},'answer_5' => {'answer_text' => '6'},'answer_6' => {'answer_text' => '7'},'answer_7' => {'answer_text' => '8'},'answer_8' => {'answer_text' => '9'},'answer_9' => {'answer_text' => '10'}}})
|
|
q.quiz_data.should be_nil
|
|
q.generate_quiz_data
|
|
q.save
|
|
|
|
data = q.stored_questions
|
|
data.length.should eql(1)
|
|
data[0][:answers].should_not be_empty
|
|
same = true
|
|
found = []
|
|
data[0][:answers].each{|a| found << a[:text] }
|
|
found.uniq.length.should eql(10)
|
|
same = false if data[0][:answers][0][:text] != '1'
|
|
same = false if data[0][:answers][1][:text] != '2'
|
|
same = false if data[0][:answers][2][:text] != '3'
|
|
same = false if data[0][:answers][3][:text] != '4'
|
|
same = false if data[0][:answers][4][:text] != '5'
|
|
same = false if data[0][:answers][5][:text] != '6'
|
|
same = false if data[0][:answers][6][:text] != '7'
|
|
same = false if data[0][:answers][7][:text] != '8'
|
|
same = false if data[0][:answers][8][:text] != '9'
|
|
same = false if data[0][:answers][9][:text] != '10'
|
|
same.should eql(false)
|
|
end
|
|
|
|
it "should shuffle questions for the quiz groups" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "some group", :pick_count => 10, :question_points => 10)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 5", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 6", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 7", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 8", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 9", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 10", 'answers' => []}, :quiz_group => g)
|
|
q.quiz_data.should be_nil
|
|
q.reload
|
|
q.generate_quiz_data
|
|
q.save
|
|
|
|
data = q.stored_questions
|
|
data.length.should eql(1)
|
|
data = data[0][:questions]
|
|
same = true
|
|
same = false if data[0][:name] != "test 1"
|
|
same = false if data[1][:name] != "test 2"
|
|
same = false if data[2][:name] != "test 3"
|
|
same = false if data[3][:name] != "test 4"
|
|
same = false if data[4][:name] != "test 5"
|
|
same = false if data[5][:name] != "test 6"
|
|
same = false if data[6][:name] != "test 7"
|
|
same = false if data[7][:name] != "test 8"
|
|
same = false if data[8][:name] != "test 9"
|
|
same = false if data[9][:name] != "test 10"
|
|
same.should eql(false)
|
|
end
|
|
|
|
it "should consider the number of questions in a group when determining the question count" do
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 10, :question_points => 2)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3", })
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4", })
|
|
q.quiz_data.should be_nil
|
|
q.generate_quiz_data
|
|
q.save
|
|
|
|
data = q.stored_questions
|
|
data.length.should eql(3)
|
|
data[0][:questions].should_not be_nil
|
|
data[1][:answers].should_not be_nil
|
|
data[2][:answers].should_not be_nil
|
|
end
|
|
|
|
context "#generate_submission" do
|
|
|
|
it "should generate a valid submission for a given user" do
|
|
u = User.create!(:name => "some user")
|
|
q = @course.quizzes.create!(:title => "some quiz")
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 1, :question_points => 2)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 5", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 6", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 7", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 8", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 9", })
|
|
q.quiz_questions.create!(:question_data => { :name => "test 10", })
|
|
q.quiz_data.should be_nil
|
|
q.generate_quiz_data
|
|
q.save
|
|
|
|
s = q.generate_submission(u)
|
|
s.state.should eql(:untaken)
|
|
s.attempt.should eql(1)
|
|
s.quiz_data.should_not be_nil
|
|
s.quiz_version.should eql(q.version_number)
|
|
s.finished_at.should be_nil
|
|
s.submission_data.should eql({})
|
|
|
|
end
|
|
|
|
it "sets end_at to lock_at when end_at is nil or after lock_at" do
|
|
lock_at = 1.minute.from_now
|
|
u = User.create!(:name => "some user")
|
|
q = @course.quizzes.create!(:title => "some quiz", :lock_at => lock_at)
|
|
# [nil, after lock_at]
|
|
[1.minute.ago, 2.minutes.from_now].each do |due_at|
|
|
q.due_at = due_at
|
|
# when
|
|
s = q.generate_submission(u)
|
|
# expect
|
|
s.end_at.should == lock_at
|
|
end
|
|
end
|
|
|
|
it "should not set end_at to lock_at if a submission is manually unlocked" do
|
|
lock_at = 1.day.ago
|
|
u = User.create!(:name => "Fred Colon")
|
|
q = @course.quizzes.create!(:title => "locked yesterday", :lock_at => lock_at)
|
|
sub = q.find_or_create_submission(u, nil, 'settings_only')
|
|
sub.manually_unlocked = true
|
|
sub.save!
|
|
sub2 = q.generate_submission(u)
|
|
sub2.end_at.should be_nil
|
|
end
|
|
end
|
|
|
|
it "should return a default title if the quiz is untitled" do
|
|
q = @course.quizzes.create!
|
|
q.quiz_title.should eql("Unnamed Quiz")
|
|
end
|
|
|
|
it "should return the assignment title if the quiz is linked to an assignment" do
|
|
a = @course.assignments.create!(:title => "some assignment")
|
|
q = @course.quizzes.create!(:assignment_id => a.id)
|
|
a.reload
|
|
q.quiz_title.should eql(a.title)
|
|
end
|
|
|
|
it "should delete the associated assignment if it is deleted" do
|
|
a = @course.assignments.create!(:title => "some assignment")
|
|
q = @course.quizzes.create!(:assignment_id => a.id, :quiz_type => "assignment")
|
|
q.assignment_id.should eql(a.id)
|
|
q.reload
|
|
q.assignment_id = nil
|
|
q.quiz_type = "practice_quiz"
|
|
q.save!
|
|
q.assignment_id.should eql(nil)
|
|
a.reload
|
|
a.should be_deleted
|
|
end
|
|
|
|
it "should reattach existing graded quiz submissions to the new assignment after a graded -> ungraded -> graded transition" do
|
|
# create a quiz
|
|
q = @course.quizzes.new
|
|
q.quiz_type = "assignment"
|
|
q.workflow_state = "available"
|
|
q.save! && q.reload
|
|
q.assignment.should_not be_nil
|
|
q.quiz_submissions.size.should == 0
|
|
|
|
# create a graded submission
|
|
q.generate_submission(User.create!(:name => "some_user")).grade_submission
|
|
q.reload
|
|
|
|
q.quiz_submissions.size.should == 1
|
|
q.quiz_submissions.first.submission.should_not be_nil
|
|
q.quiz_submissions.first.submission.assignment.should == q.assignment
|
|
|
|
# switch to ungraded
|
|
q.quiz_type = "practice_quiz"
|
|
q.save! && q.reload
|
|
q.assignment.should be_nil
|
|
q.quiz_submissions.size.should == 1
|
|
|
|
# switch back to graded
|
|
q.quiz_type = "assignment"
|
|
q.save! && q.reload
|
|
q.assignment.should_not be_nil
|
|
q.quiz_submissions.size.should == 1
|
|
q.quiz_submissions.first.submission.should_not be_nil
|
|
q.quiz_submissions.first.submission.assignment.should == q.assignment
|
|
end
|
|
|
|
context "clone_for" do
|
|
it "should clone for other contexts" do
|
|
u = User.create!(:name => "some user")
|
|
q = @course.quizzes.create!(:title => "some quiz")
|
|
q = @course.quizzes.create!(:title => "new quiz")
|
|
g = q.quiz_groups.create!(:name => "group 1", :pick_count => 1, :question_points => 2)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 2", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 3", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 4", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 5", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 6", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 7", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 8", }, :quiz_group => g)
|
|
q.quiz_questions.create!(:question_data => { :name => "test 9", })
|
|
q.quiz_questions.create!(:question_data => { :name => "test 10", })
|
|
q.quiz_data.should be_nil
|
|
q.generate_quiz_data
|
|
q.save
|
|
course
|
|
new_q = q.clone_for(@course)
|
|
new_q.context.should eql(@course)
|
|
new_q.context.should_not eql(q.context)
|
|
new_q.title.should eql(q.title)
|
|
new_q.quiz_groups.length.should eql(q.quiz_groups.length)
|
|
new_q.quiz_questions.length.should eql(q.quiz_questions.length)
|
|
new_q.quiz_questions.first.question_data[:id].should be_nil
|
|
new_q.quiz_questions.first.data[:id].should == new_q.quiz_questions.first.id
|
|
end
|
|
|
|
it "should set the related assignment's group correctly" do
|
|
ag = @course.assignment_groups.create!(:name => 'group')
|
|
a = @course.assignments.create!(:title => "some assignment", :points_possible => 5, :assignment_group => ag)
|
|
a.points_possible.should eql(5.0)
|
|
a.submission_types.should_not eql("online_quiz")
|
|
q = @course.quizzes.build(:assignment_id => a.id, :title => "some quiz", :points_possible => 10)
|
|
q.workflow_state = 'available'
|
|
q.save
|
|
|
|
course
|
|
new_q = q.clone_for(@course)
|
|
new_q.context.should eql(@course)
|
|
new_q.context.should_not eql(q.context)
|
|
new_q.assignment.assignment_group.should_not eql(ag)
|
|
new_q.assignment.assignment_group.context.should eql(@course)
|
|
end
|
|
|
|
it "should not blow up when a quiz question has a link to the quiz it's in" do
|
|
q = @course.quizzes.create!(:title => "some quiz")
|
|
question_text = "<a href='/courses/#{@course.id}/quizzes/#{q.id}/edit'>hi</a>"
|
|
q.quiz_questions.create!(:question_data => { :name => "test 1", :question_text => question_text })
|
|
q.generate_quiz_data
|
|
q.save
|
|
course
|
|
new_q = q.clone_for(@course)
|
|
new_q.quiz_questions.first.question_data[:question_text].should match /\/courses\/#{@course.id}\/quizzes\/#{new_q.id}\/edit/
|
|
end
|
|
|
|
it "should only create one associated assignment for a graded quiz" do
|
|
q = @course.quizzes.create!(:title => "graded quiz", :quiz_type => 'assignment')
|
|
q.workflow_state = 'available'
|
|
q.save
|
|
course
|
|
expect {
|
|
new_q = q.clone_for(@course)
|
|
}.to change(@course.assignments, :count).by(1)
|
|
end
|
|
end
|
|
|
|
describe "Quiz with QuestionGroup pointing to QuestionBank" do
|
|
before(:each) do
|
|
course_with_student
|
|
@bank = @course.assessment_question_banks.create!(:title=>'Test Bank')
|
|
@bank.assessment_questions.create!(:question_data => {'name' => 'Group Question 1', :question_type=>'essay_question', :question_text=>'gq1', 'answers' => []})
|
|
@bank.assessment_questions.create!(:question_data => {'name' => 'Group Question 2', :question_type=>'essay_question', :question_text=>'gq2', 'answers' => []})
|
|
@quiz = @course.quizzes.create!(:title => "i'm tired quiz")
|
|
@quiz.quiz_questions.create!(:question_data => { :name => "Quiz Question 1", :question_type=>'essay_question', :question_text=>'qq1', 'answers' => [], :points_possible=>5.0})
|
|
@group = @quiz.quiz_groups.create!(:name => "question group", :pick_count => 3, :question_points => 5.0)
|
|
@group.assessment_question_bank = @bank
|
|
@group.save!
|
|
@quiz.generate_quiz_data
|
|
@quiz.save!
|
|
@quiz.reload
|
|
end
|
|
|
|
it "should create a submission" do
|
|
submission = @quiz.generate_submission(@user)
|
|
submission.quiz_data.length.should == 3
|
|
texts = submission.quiz_data.map{|q|q[:question_text]}
|
|
texts.member?('gq1').should be_true
|
|
texts.member?('gq2').should be_true
|
|
texts.member?('qq1').should be_true
|
|
end
|
|
|
|
it "should get the correct points possible" do
|
|
@quiz.current_points_possible.should == 15
|
|
end
|
|
|
|
it "should omit top level questions when selecting from a question bank" do
|
|
questions = @bank.assessment_questions
|
|
# add the first question directly onto the quiz, so it shouldn't get "randomly" selected from the group
|
|
linked_question = @quiz.quiz_questions.build(:question_data => questions[0].question_data)
|
|
linked_question.assessment_question_id = questions[0].id
|
|
linked_question.save!
|
|
@quiz.generate_quiz_data
|
|
@quiz.save!
|
|
@quiz.reload
|
|
|
|
submission = @quiz.generate_submission(@user)
|
|
submission.quiz_data.length.should == 3
|
|
texts = submission.quiz_data.map{|q|q[:question_text]}
|
|
texts.member?('gq1').should be_true
|
|
texts.member?('gq2').should be_true
|
|
texts.member?('qq1').should be_true
|
|
end
|
|
|
|
end
|
|
|
|
it "should ignore lockdown-browser setting if that plugin is not enabled" do
|
|
q = @course.quizzes.build(:title => "some quiz")
|
|
q1 = @course.quizzes.build(:title => "some quiz", :require_lockdown_browser => true, :require_lockdown_browser_for_results => false)
|
|
q2 = @course.quizzes.build(:title => "some quiz", :require_lockdown_browser => true, :require_lockdown_browser_for_results => true)
|
|
|
|
# first, disable any lockdown browsers that might be configured already
|
|
Canvas::Plugin.all_for_tag(:lockdown_browser).each { |p| p.settings[:enabled] = false }
|
|
|
|
# nothing should be restricted
|
|
Quiz.lockdown_browser_plugin_enabled?.should be_false
|
|
[q, q1, q2].product([:require_lockdown_browser, :require_lockdown_browser?, :require_lockdown_browser_for_results, :require_lockdown_browser_for_results?]).
|
|
each { |qs| qs[0].send(qs[1]).should be_false }
|
|
|
|
# register a plugin
|
|
Canvas::Plugin.register(:example_spec_lockdown_browser, :lockdown_browser, {
|
|
:settings => {:enabled => false}})
|
|
|
|
# nothing should change yet
|
|
Quiz.lockdown_browser_plugin_enabled?.should be_false
|
|
[q, q1, q2].product([:require_lockdown_browser, :require_lockdown_browser?, :require_lockdown_browser_for_results, :require_lockdown_browser_for_results?]).
|
|
each { |qs| qs[0].send(qs[1]).should be_false }
|
|
|
|
# now actually enable the plugin
|
|
setting = PluginSetting.find_or_create_by_name('example_spec_lockdown_browser')
|
|
setting.settings = {:enabled => true}
|
|
setting.save!
|
|
|
|
# now the restrictions should take effect
|
|
Quiz.lockdown_browser_plugin_enabled?.should be_true
|
|
[:require_lockdown_browser, :require_lockdown_browser?, :require_lockdown_browser_for_results, :require_lockdown_browser_for_results?].
|
|
each { |s| q.send(s).should be_false }
|
|
[:require_lockdown_browser, :require_lockdown_browser?].
|
|
each { |s| q1.send(s).should be_true }
|
|
[:require_lockdown_browser_for_results, :require_lockdown_browser_for_results?].
|
|
each { |s| q1.send(s).should be_false }
|
|
[:require_lockdown_browser, :require_lockdown_browser?, :require_lockdown_browser_for_results, :require_lockdown_browser_for_results?].
|
|
each { |s| q2.send(s).should be_true }
|
|
end
|
|
|
|
describe "non_shuffled_questions" do
|
|
subject { Quiz.non_shuffled_questions }
|
|
|
|
it { should include "true_false_question" }
|
|
it { should include "matching_question" }
|
|
it { should include "fill_in_multiple_blanks_question" }
|
|
it { should_not include "multiple_choice_question" }
|
|
end
|
|
|
|
describe "prepare_answers" do
|
|
let(:quiz) { Quiz.new }
|
|
let(:question) { { :answers => answers } }
|
|
let(:answers) { ['a', 'b', 'c'] }
|
|
|
|
context "on a shuffle answers question" do
|
|
before { quiz.stubs(:shuffle_answers).returns(true) }
|
|
|
|
context "on a non-shuffleable question type" do
|
|
before { Quiz.stubs(:shuffleable_question_type?).returns(false) }
|
|
|
|
it "doesn't shuffle" do
|
|
quiz.prepare_answers(question).should == answers
|
|
end
|
|
end
|
|
|
|
context "on a shuffleable question type" do
|
|
before { Quiz.stubs(:shuffleable_question_type?).returns(true) }
|
|
|
|
it "returns the same answers, not necessarily in the same order" do
|
|
quiz.prepare_answers(question).sort.should == answers.sort
|
|
end
|
|
|
|
it "shuffles" do
|
|
answers.expects(:sort_by)
|
|
quiz.prepare_answers(question)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "on a non-shuffle answers question" do
|
|
it "doesn't shuffle" do
|
|
quiz.prepare_answers(question).should == answers
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "shuffleable_question_type?" do
|
|
specify { Quiz.shuffleable_question_type?("true_false_question").should be_false }
|
|
specify { Quiz.shuffleable_question_type?("multiple_choice_question").should be_true }
|
|
end
|
|
|
|
describe '#has_student_submissions?' do
|
|
before do
|
|
course = Course.create!
|
|
@quiz = Quiz.create!(:context => course)
|
|
@user = User.create!
|
|
@enrollment = @user.student_enrollments.create!(:course => course)
|
|
@enrollment.update_attribute(:workflow_state, 'active')
|
|
@submission = QuizSubmission.create!(:quiz => @quiz, :user => @user)
|
|
@submission.update_attribute(:workflow_state, 'untaken')
|
|
end
|
|
|
|
it 'returns true if the submission is not settings_only and its user is part of this course' do
|
|
@submission.settings_only?.should be_false
|
|
@quiz.context.students.include?(@user).should be_true
|
|
@quiz.has_student_submissions?.should be_true
|
|
end
|
|
|
|
it 'is false if the submission is settings_only' do
|
|
@submission.update_attribute(:workflow_state, 'settings_only')
|
|
@quiz.has_student_submissions?.should be_false
|
|
end
|
|
|
|
it 'is false if the user is not part of this course' do
|
|
@user.student_enrollments.delete_all
|
|
@quiz.has_student_submissions?.should be_false
|
|
end
|
|
|
|
it 'is false if there are no submissions' do
|
|
@quiz.quiz_submissions.delete_all
|
|
@quiz.has_student_submissions?.should be_false
|
|
end
|
|
|
|
it 'is true if only one submission of many matches the conditions' do
|
|
QuizSubmission.create!(:quiz => @quiz, :user => User.create!)
|
|
@quiz.has_student_submissions?.should be_true
|
|
end
|
|
end
|
|
|
|
describe "#group_category_id" do
|
|
|
|
it "returns the assignment's group category id if it has an assignment" do
|
|
quiz = Quiz.new(:title => "Assignment Group Category Quiz")
|
|
quiz.expects(:assignment).returns stub(:group_category_id => 1)
|
|
quiz.group_category_id.should == 1
|
|
end
|
|
|
|
it "returns nil if it doesn't have an assignment" do
|
|
quiz = Quiz.new(:title => "Quiz w/o assignment")
|
|
quiz.group_category_id.should be_nil
|
|
end
|
|
|
|
end
|
|
|
|
describe "linking overrides with assignments" do
|
|
let(:course) { course_model }
|
|
let(:quiz) { quiz_model(:course => course, :due_at => 5.days.from_now).reload }
|
|
let(:override) { assignment_override_model(:quiz => quiz) }
|
|
let(:override_student) { override.assignment_override_students.build }
|
|
|
|
before do
|
|
override.override_due_at(7.days.from_now)
|
|
override.save!
|
|
|
|
student_in_course(:course => course)
|
|
override_student.user = @student
|
|
override_student.save!
|
|
end
|
|
|
|
context "before the quiz has an assignment" do
|
|
context "override" do
|
|
it "has a quiz" do
|
|
override.quiz.should == quiz
|
|
end
|
|
|
|
it "has a nil assignment" do
|
|
override.assignment.should be_nil
|
|
end
|
|
end
|
|
|
|
context "override student" do
|
|
it "has a quiz" do
|
|
override_student.quiz.should == quiz
|
|
end
|
|
|
|
it "has a nil assignment" do
|
|
override_student.assignment.should be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context "once the quiz is published" do
|
|
before do
|
|
# publish the quiz
|
|
quiz.workflow_state = 'available'
|
|
quiz.save
|
|
override.reload
|
|
override_student.reload
|
|
quiz.assignment.reload
|
|
end
|
|
|
|
context "override" do
|
|
it "has a quiz" do
|
|
override.quiz.should == quiz
|
|
end
|
|
|
|
it "has the quiz's assignment" do
|
|
override.assignment.should == quiz.assignment
|
|
end
|
|
|
|
it "has the quiz's assignment's version number" do
|
|
override.assignment_version.should == quiz.assignment.version_number
|
|
end
|
|
|
|
it "has the quiz's version number" do
|
|
override.quiz_version.should == quiz.version_number
|
|
end
|
|
|
|
end
|
|
|
|
context "override student" do
|
|
it "has a quiz" do
|
|
override_student.quiz.should == quiz
|
|
end
|
|
|
|
it "has the quiz's assignment" do
|
|
override_student.assignment.should == quiz.assignment
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the assignment ID doesn't change" do
|
|
it "doesn't update overrides" do
|
|
quiz.expects(:link_assignment_overrides).once
|
|
# publish the quiz
|
|
quiz.workflow_state = 'available'
|
|
quiz.save
|
|
quiz.expects(:link_assignment_overrides).never
|
|
quiz.save
|
|
end
|
|
end
|
|
|
|
context "when the assignment ID changes" do
|
|
it "links overrides" do
|
|
quiz.expects(:link_assignment_overrides).once
|
|
quiz.workflow_state = 'available'
|
|
quiz.save!
|
|
quiz.expects(:link_assignment_overrides).once
|
|
quiz.assignment = nil
|
|
quiz.assignment_id = 345
|
|
quiz.save!
|
|
end
|
|
end
|
|
end
|
|
|
|
context "custom validations" do
|
|
context "quiz_type" do
|
|
it "should not save an invalid quiz_type" do
|
|
quiz = @course.quizzes.create! :title => "test quiz"
|
|
quiz.quiz_type = "totally_invalid_quiz_type"
|
|
quiz.save.should be_false
|
|
quiz.errors["invalid_quiz_type"].should be_present
|
|
end
|
|
|
|
it "should not validate quiz_type if not changed" do
|
|
quiz = @course.quizzes.build :title => "test quiz", :quiz_type => 'invalid'
|
|
quiz.save(false).should be_true # save without validation
|
|
quiz.reload
|
|
quiz.save.should be_true
|
|
quiz.errors.should be_blank
|
|
quiz.quiz_type.should == 'invalid'
|
|
end
|
|
end
|
|
|
|
context "ip_filter" do
|
|
it "should not save an invalid ip_filter" do
|
|
quiz = @course.quizzes.create! :title => "test quiz"
|
|
quiz.ip_filter = "999.999.1942.489"
|
|
quiz.save.should be_false
|
|
quiz.errors["invalid_ip_filter"].should be_present
|
|
end
|
|
|
|
it "should not validate ip_filter if not changed" do
|
|
quiz = @course.quizzes.build :title => "test quiz", :ip_filter => '123.fourfivesix'
|
|
quiz.save(false).should be_true # save without validation
|
|
quiz.reload
|
|
quiz.save.should be_true
|
|
quiz.errors.should be_blank
|
|
quiz.ip_filter.should == '123.fourfivesix'
|
|
end
|
|
end
|
|
|
|
context "hide_results" do
|
|
it "should not save an invalid hide_results" do
|
|
quiz = @course.quizzes.create! :title => "test quiz"
|
|
quiz.hide_results = "totally_invalid_value"
|
|
quiz.save.should be_false
|
|
quiz.errors["invalid_hide_results"].should be_present
|
|
end
|
|
|
|
it "should not validate hide_results if not changed" do
|
|
quiz = @course.quizzes.build :title => "test quiz", :hide_results => 'invalid'
|
|
quiz.save(false).should be_true # save without validation
|
|
quiz.reload
|
|
quiz.save.should be_true
|
|
quiz.errors.should be_blank
|
|
quiz.hide_results.should == 'invalid'
|
|
end
|
|
end
|
|
end
|
|
end
|