qti 2 conformance

test plan:
* qti migration regressions

Change-Id: I53587880a591d9ea70e7039d4216770cb1772880
Reviewed-on: https://gerrit.instructure.com/34407
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Clare Strong <clare@instructure.com>
Product-Review: James Williams  <jamesw@instructure.com>
This commit is contained in:
Bracken Mosbacker 2014-05-06 11:48:19 -05:00 committed by James Williams
parent 463407ee65
commit 12ec269bba
25 changed files with 391 additions and 39 deletions

View File

@ -124,6 +124,9 @@ module Importers
migration.add_imported_item(bank) if migration
self.prep_for_import(hash, context, migration)
missing_links = hash.delete(:missing_links) || {}
import_warnings = hash.delete(:import_warnings)
if id = hash['assessment_question_id']
AssessmentQuestion.where(id: id).update_all(name: hash[:question_name], question_data: hash.to_yaml,
workflow_state: 'active', created_at: Time.now.utc, updated_at: Time.now.utc,
@ -137,21 +140,21 @@ module Importers
AssessmentQuestion.primary_key, nil, AssessmentQuestion.sequence_name)
hash['assessment_question_id'] = id
end
if migration
hash[:missing_links].each do |field, missing_links|
missing_links.each do |field, links|
migration.add_missing_content_links(:class => self.to_s,
:id => hash['assessment_question_id'], :field => field, :missing_links => missing_links,
:id => hash['assessment_question_id'], :field => field, :missing_links => links,
:url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text")
end
if hash[:import_warnings]
hash[:import_warnings].each do |warning|
if import_warnings
import_warnings.each do |warning|
migration.add_warning(warning, {
:fix_issue_html_url => "/#{context.class.to_s.underscore.pluralize}/#{context.id}/question_banks/#{bank.id}#question_#{hash['assessment_question_id']}_question_text"
})
end
end
end
hash.delete(:missing_links)
hash
end

View File

@ -44,6 +44,7 @@ module Importers
begin
Importers::QuizImporter.import_from_migration(assessment, migration.context, migration, question_data, nil, allow_update)
rescue
debugger
migration.add_import_warning(t('#migration.quiz_type', "Quiz"), assessment[:title], $!)
end
end
@ -116,9 +117,7 @@ module Importers
if question_data
hash[:questions] ||= []
if question_data[:qq_data] || question_data[:aq_data]
existing_questions = item.quiz_questions.active.where("migration_id IS NOT NULL").select([:id, :migration_id]).index_by(&:migration_id)
end
existing_questions = item.quiz_questions.active.where("migration_id IS NOT NULL").select([:id, :migration_id]).index_by(&:migration_id)
if question_data[:qq_data]
question_data[:qq_data].values.each do |q|
@ -158,10 +157,17 @@ module Importers
when "question_group"
Importers::QuizGroupImporter.import_from_migration(question, context, item, question_data, i + 1, migration)
when "text_only_question"
qq = item.quiz_questions.new
qq.question_data = question
qq.position = i + 1
qq.save!
if question['migration_id']
existing_question = existing_questions[question['migration_id']]
question['quiz_question_id'] = existing_question.id if existing_question
question['position'] = i + 1
Importers::QuizQuestionImporter.import_from_migration(question, context, migration, item)
else
qq = item.quiz_questions.new
qq.question_data = question
qq.position = i + 1
qq.save!
end
end
end
end

View File

@ -54,7 +54,7 @@ module Qti
def self.convert_assessments(manifest_path, opts={})
assessments = []
doc = Nokogiri::XML(open(manifest_path))
doc.css('manifest resources resource[type=imsqti_assessment_xmlv2p1]').each do |item|
doc.css('manifest resources resource[type=imsqti_assessment_xmlv2p1], manifest resources resource[type=imsqti_test_xmlv2p1]').each do |item|
a = AssessmentTestConverter.new(item, File.dirname(manifest_path), opts).create_instructure_quiz
assessments << a if a
end

View File

@ -58,26 +58,51 @@ class AssessmentItemConverter
end
end
EXCLUDED_QUESTION_TEXT_CLASSES = ["RESPONSE_BLOCK", "RIGHT_MATCH_BLOCK"]
def create_instructure_question
begin
create_doc
@question[:question_name] = @title || get_node_att(@doc, 'assessmentItem', 'title')
# The colons are replaced with dashes in the conversion from QTI 1.2
@question[:migration_id] = get_node_att(@doc, 'assessmentItem', 'identifier')
@question[:migration_id] = @question[:migration_id].gsub(/:/, '-') if @question[:migration_id]
@question[:migration_id] = @question[:migration_id].gsub(/:/, '-').gsub('identifier=', '') if @question[:migration_id]
if @flavor == Qti::Flavors::D2L
# In D2L-generated QTI the assessments reference the items by the label instead of the identifier
# also, the identifier is not always unique, so we use the label as the migration id
@question[:migration_id] = get_node_att(@doc, 'assessmentItem', 'label')
end
if @doc.at_css('itemBody div.html')
@question[:question_text] = ''
@doc.css('itemBody > div.html').each_with_index do |text, i|
@question[:question_text] += "\n<br/>\n" if i > 0
@question[:question_text] += sanitize_html_string(text.text)
if @type == 'text_entry_interaction'
@doc.css('textEntryInteraction').each do |node|
node.inner_html = "[#{node['responseIdentifier']}]"
end
end
parse_instructure_metadata
selectors = ['itemBody > div', 'itemBody > p']
type = @opts[:custom_type] || @migration_type || @type
unless ['fill_in_multiple_blanks_question', 'canvas_matching', 'matching_question',
'multiple_dropdowns_question', 'respondus_matching'].include?(type)
selectors << 'itemBody > choiceInteraction > prompt'
end
text_nodes = @doc.css(selectors.join(','))
text_nodes = text_nodes.reject{|node| node.inner_html.strip.empty? ||
EXCLUDED_QUESTION_TEXT_CLASSES.any?{|c| c.casecmp(node['class'].to_s) == 0}}
if text_nodes.length > 0
@question[:question_text] = ''
text_nodes.each_with_index do |node, i|
@question[:question_text] += "\n<br/>\n" if i > 0
if ['html', 'text'].include?(node['class'])
@question[:question_text] += sanitize_html_string(node.text)
else
@question[:question_text] += sanitize_html!(node)
end
end
elsif text = @doc.at_css('itemBody > div.text')
@question[:question_text] = sanitize_html!(text)
elsif text = @doc.at_css('itemBody div:first-child') || @doc.at_css('itemBody p:first-child') || @doc.at_css('itemBody div') || @doc.at_css('itemBody p')
@question[:question_text] = sanitize_html!(text)
elsif @doc.at_css('itemBody')
@ -85,7 +110,6 @@ class AssessmentItemConverter
@question[:question_text] = sanitize_html_string(text.text)
end
end
parse_instructure_metadata
if @migration_type and UNSUPPORTED_TYPES.member?(@migration_type)
@question[:question_type] = @migration_type
@ -109,6 +133,7 @@ class AssessmentItemConverter
/matching/i => 'matching_question',
'textInformation' => 'text_only_question',
'trueFalse' => 'true_false_question',
'multiple_dropdowns' => 'multiple_dropdowns_question'
}
def parse_instructure_metadata
@ -163,17 +188,13 @@ class AssessmentItemConverter
end
def unique_local_id
@@ids ||= {}
id = rand(10000)
while @@ids[id]
id = rand(10000)
end
@@ids[id] = true
id
@@idx ||= 0
@@idx += 1
"qti_item_migration_id_#{@identifier}_#{@@idx}"
end
def reset_local_ids
@@ids = {}
@@idx = 0
end
def get_feedback
@ -348,7 +369,7 @@ class AssessmentItemConverter
guesser = QuestionTypeEducatedGuesser.new(opts)
opts[:interaction_type], opts[:custom_type] = guesser.educatedly_guess_type
end
case opts[:interaction_type]
when /choiceinteraction|multiple_choice_question|multiple_answers_question|true_false_question|stupid_likert_scale_question/i
if opts[:custom_type] and opts[:custom_type] == "matching"
@ -372,6 +393,8 @@ class AssessmentItemConverter
q = OrderInteraction.new(opts)
when /fill_in_multiple_blanks_question|multiple_dropdowns_question/i
q = FillInTheBlank.new(opts)
when /textentryinteraction/i
q = FillInTheBlank.new(opts)
when nil
q = AssessmentItemConverter.new(opts)
else

View File

@ -89,7 +89,7 @@ class AssessmentTestConverter
if limit = doc.at_css('timeLimits')
@quiz[:time_limit] = AssessmentTestConverter.parse_time_limit(limit['maxTime'])
end
if part = doc.at_css('testPart[identifier=BaseTestPart]')
if part = doc.at_css('testPart[identifier=BaseTestPart]') || doc.at_css('testPart')
if control = part.at_css('itemSessionControl')
if max = control['maxAttempts']
max = -1 if max =~ /unlimited/i
@ -153,14 +153,14 @@ class AssessmentTestConverter
if val = get_bool_val(section, 'sourcebank_is_external')
group[:question_bank_is_external] = val
end
group[:migration_id] = section['identifier'] && section['identifier'] != "" ? section['identifier'] : rand(100_000)
group[:migration_id] = section['identifier'] && section['identifier'] != "" ? section['identifier'] : unique_local_id
questions_list = group[:questions]
end
end
if section['visible'] and section['visible'] =~ /true/i
if title = section['title']
#Create an empty question with a title in it
@quiz[:questions] << {:question_type => 'text_only_question', :question_text => title, :migration_id => rand(100_000)}
@quiz[:questions] << {:question_type => 'text_only_question', :question_text => title, :migration_id => unique_local_id}
end
end
@ -222,5 +222,10 @@ class AssessmentTestConverter
weight
end
def unique_local_id
@@idx ||= 0
@@idx += 1
"qti_test_migration_id_#{@identifier}_#{@@idx}"
end
end
end

View File

@ -13,6 +13,8 @@ class Converter < Canvas::Migration::Migrator
QTI_2_NAMESPACES = %w[
http://www.imsglobal.org/xsd/imsqti_v2p0
http://www.imsglobal.org/xsd/imsqti_v2p1
http://www.imsglobal.org/xsd/qti/qtiv2p0
http://www.imsglobal.org/xsd/qti/qtiv2p1
]
IMS_MD = "http://www.imsglobal.org/xsd/imsmd_v1p2"
QTI_2_OUTPUT_PATH = "qti_2_1"
@ -63,7 +65,11 @@ class Converter < Canvas::Migration::Migrator
def self.is_qti_2(manifest_path)
if File.exists?(manifest_path)
xml = Nokogiri::XML(File.open(manifest_path))
return xml.namespaces.values.any? { |v| QTI_2_NAMESPACES.include?(v) }
if xml.namespaces.values.any? { |v| QTI_2_NAMESPACES.include?(v) }
return true
elsif (xml.at_css('metadata schema') ? xml.at_css('metadata schema').text : '') =~ /QTIv2\./i
return true
end
end
false
end

View File

@ -16,6 +16,8 @@ class FillInTheBlank < AssessmentItemConverter
process_angel
elsif @type == 'fillinmultiple'
process_respondus
elsif @type == 'text_entry_interaction'
process_text_entry
elsif @doc.at_css('itemBody extendedTextInteraction')
process_d2l
else
@ -130,5 +132,19 @@ class FillInTheBlank < AssessmentItemConverter
end
end
def process_text_entry
@doc.css('responseDeclaration').each do |res_node|
res_id = res_node['identifier']
res_node.css('correctResponse value').each do |correct_id|
answer = {}
answer[:id] = unique_local_id
answer[:weight] = DEFAULT_CORRECT_WEIGHT
answer[:text] = correct_id.text
answer[:blank_id] = res_id
@question[:answers] << answer
end
end
end
end
end

View File

@ -18,9 +18,15 @@ class QuestionTypeEducatedGuesser < AssessmentItemConverter
if @doc.at_css('associateInteraction')
return ['associateinteraction', 'matching']
end
if@doc.at_css('extendedTextInteraction')
if @doc.at_css('extendedTextInteraction')
return ['extendedTextInteraction', nil]
end
if @doc.at_css('textEntryInteraction')
return ['textEntryInteraction', 'text_entry_interaction']
end
if @doc.at_css('matchInteraction')
return ['matchInteraction', 'matching']
end
rescue => e
message = "There was an error educatedly guessing the type for an assessment question"
@question[:qti_error] = "#{message} - #{e.to_s}"

View File

@ -167,10 +167,10 @@ module CanvasExpected
:question_name=>"Oi!",
:points_possible=>10.3,
:migration_id=>"if87ef626591c52375b6a4f16cdab8bd0",
:question_text=>"Ole"}
:question_text=>"Ole\n<br/>\n<a>Test Page</a>\n<br/>\nWhy would you link to a wiki page from a quiz question? That doesn't seem right."}
TRUE_FALSE = {:points_possible=>10,
:question_text=>"Generating QTI is \n<strong>super</strong> awesome!",
:question_text=>"Generating QTI is \n<strong>super</strong> awesome!\n<br/>\noh, and &amp;amp;",
:answers=>
[{:weight=>0, :migration_id=>"RESPONSE_5309", :text=>"True"},
{:comments=>"You're an idiot.", :weight=>100, :migration_id=>"RESPONSE_239",:text=>"False"}],

View File

@ -0,0 +1,287 @@
require File.expand_path(File.dirname(__FILE__) + '/../../qti_helper')
if Qti.migration_executable
# QTI conformancec packages from http://www.imsglobal.org/developers/apipalliance/conformance/QTIconformanceresources.cfm
describe "QTI 2.1 zip" do
def import_fixture(filename)
archive_file_path = File.join(BASE_FIXTURE_DIR, 'qti2_conformance', filename)
unzipped_file_path = File.join(File.dirname(archive_file_path), "qti_#{File.basename(archive_file_path, '.zip')}", 'oi')
@export_folder = File.join(File.dirname(archive_file_path), "qti_#{filename}".gsub('.zip', ''))
@course = Course.create!(:name => filename)
@migration = ContentMigration.create(:context => @course)
@converter = Qti::Converter.new(:export_archive_path => archive_file_path, :base_download_dir => unzipped_file_path, :content_migration => @migration)
@converter.export
@course_data = @converter.course.with_indifferent_access
@course_data['all_files_export'] ||= {}
@course_data['all_files_export']['file_path'] = @course_data['all_files_zip']
@migration.migration_settings[:migration_ids_to_import] = {:copy => {}}
@migration.migration_settings[:files_import_root_path] = @course_data[:files_import_root_path]
Importers::CourseContentImporter.import_content(@course, @course_data, nil, @migration)
@migration.migration_issues.should be_empty
end
after :each do
if @export_folder && File.exists?(@export_folder)
FileUtils::rm_rf(@export_folder)
end
end
it "should import VE_IP_01" do
import_fixture('VE_IP_01.zip')
@course.quizzes.count.should == 0
@course.assessment_questions.count.should == 1
q = @course.assessment_questions.first
q.name.should == "QTI v2.1 Entry Profile Single T/F Item Test Instance"
q.question_data['question_text'].should include("Answer the following question.")
q.question_data['question_text'].should include("Sigmund Freud and Carl Jung both belong to the psychoanalytic school of psychology.")
q.question_data['question_type'].should == 'multiple_choice_question'
q.question_data['answers'].count.should == 2
answers = q.question_data['answers'].sort_by{|h| h['migration_id']}
answers.map{|a| a['text']}.sort.should == ['False', 'True']
answers.map{|a| a['weight']}.sort.should == [0, 100]
end
it "should import VE_IP_02" do
import_fixture('VE_IP_02.zip')
@course.quizzes.count.should == 0
@course.assessment_questions.count.should == 1
q = @course.assessment_questions.first
q.attachments.count.should == 1
att = q.attachments.first
q.name.should == "QTI v2.1 Entry Profile Single MC/SR Item Test Instance"
["<img id=\"figure1\" height=\"165\" width=\"250\" src=\"/assessment_questions/#{q.id}/files/#{att.id}/download?verifier=#{att.uuid}\" alt=\"Figure showing Rectangle ABCD divided into 12 equal boxes. 4 of the boxes are shaded.\">",
"<span id=\"labelA\">A</span>", "<span id=\"labelB\">B</span>", "<span id=\"labelC\">C</span>", "<span id=\"labelD\">D</span>",
"In the figure above, what fraction of the rectangle <em>ABCD</em> is", "shaded?"
].each do |text|
q.question_data['question_text'].should include(text)
end
q.question_data['question_type'].should == 'multiple_choice_question'
answers = q.question_data['answers'].sort_by{|h| h['migration_id']}
answers.count.should == 5
answers.map{|h| h['weight']}.should == [0, 0, 0, 100, 0]
end
it "should import VE_IP_03" do
import_fixture('VE_IP_03.zip')
@course.quizzes.count.should == 0
@course.assessment_questions.count.should == 1
q = @course.assessment_questions.first
q.name.should == "QTI v2.1 Entry Profile Single MC/MR Item Test Instance"
q.question_data['question_text'].split("\n").map(&:strip).should == [
"<span id=\"a\">Ms. Smith's class contains 24 students. </span>",
"<span id=\"b\">Each student voted for his or her favorite color. </span>",
"<span id=\"c\">The result of the class vote is shown </span>",
"<span id=\"z\">in the table below.</span>", "<br>",
"Indicate which of the following statements are accurate."
]
q.question_data['question_type'].should == 'multiple_answers_question'
answers = q.question_data['answers'].sort_by{|h| h['migration_id']}
answers.count.should == 5
answers.map{|h| h['weight']}.should == [100, 100, 0, 100, 0]
answers.map{|h| h['text']}.should == [
"The majority of students voted for Red.",
"Twice as many students voted for Red a voted for Blue.",
"Two percent of students voted for Yellow.",
"Red received more votes than any other color.",
"Twenty-five percent of students voted for Green."
]
end
it "should import VE_IP_04" do
import_fixture('VE_IP_04.zip')
@course.quizzes.count.should == 0
@course.assessment_questions.count.should == 1
q = @course.assessment_questions.first
q.name.should == "QTI v2.1 Entry Profile Single FIB Item Test Instance"
q.question_data['question_text'].should include("Canada and the United States share 4 out of the 5 Great Lakes in central North America.")
q.question_data['question_text'].should include("Which lake is entirely within the boundaries of the United States?")
q.question_data['question_text'].should include("Type your answer here: [RESPONSE]")
q.question_data['question_type'].should == 'fill_in_multiple_blanks_question'
q.question_data['answers'].count.should == 1
answer = q.question_data['answers'].first
answer['weight'].should == 100
answer['text'].should == 'Lake Michigan'
answer['blank_id'].should == 'RESPONSE'
end
it "should import VE_IP_05" do
import_fixture('VE_IP_05.zip')
@course.quizzes.count.should == 0
@course.assessment_questions.count.should == 1
q = @course.assessment_questions.first
q.name.should == "QTI v2.1 Entry Profile Single Essay Item Test Instance"
q.attachments.count.should == 3
q.attachments.each do |att|
q.question_data['question_text'].should include("src=\"/assessment_questions/#{q.id}/files/#{att.id}/download?verifier=#{att.uuid}\"")
end
q.question_data['question_type'].should == 'essay_question'
q.question_data['answers'].count.should == 0
end
it "should import VE_IP_06" do
pending('hotspot questions')
import_fixture('VE_IP_06.zip')
end
it "should import VE_IP_07" do
import_fixture('VE_IP_07.zip')
@course.quizzes.count.should == 0
@course.assessment_questions.count.should == 1
q = @course.assessment_questions.first
q.name.should == "QTI v2.1 Core Profile Single Pattern Match Item Test Instance"
q.question_data['question_text'].split("\n").map(&:strip).select{|s| s.length > 0}.should == [
"Match the following characters to the Shakespeare play they appeared in:",
"Capulet", "Demetrius", "Lysander", "Prospero",
"A Midsummer-Night's Dream", "Romeo and Juliet", "The Tempest"
]
q.question_data['question_type'].should == 'matching_question'
answers = q.question_data['answers'].sort_by{|h| h['text']}
answers.count.should == 4
matches = q.question_data['matches']
matches.count.should == 3
answers.map{|h| h['text']}.should == ["Capulet", "Demetrius", "Lysander", "Prospero"]
answers.map{|h| h['right']}.should == [
"Romeo and Juliet",
"A Midsummer-Night's Dream",
"A Midsummer-Night's Dream",
"The Tempest"
]
answers.each do |h|
match = matches.detect{|m| m['match_id'] == h['match_id']}
match['text'].should == h['right']
end
end
it "should import VE_IP_11" do
import_fixture('VE_IP_11.zip')
@course.assessment_questions.count.should == 5
@course.assessment_questions.map{|q| q.question_data['question_type']}.sort.should == [
"essay_question",
"fill_in_multiple_blanks_question",
"multiple_answers_question",
"multiple_choice_question",
"multiple_choice_question"
]
end
it "should import VE_TP_01" do
import_fixture('VE_TP_01.zip')
@course.assessment_questions.count.should == 1
@course.quizzes.count.should == 1
quiz = @course.quizzes.first
quiz.quiz_questions.count.should == 2
header = quiz.quiz_questions.detect{|q| q.position == 1}
header.question_data['question_type'].should == 'text_only_question'
header.question_data['question_text'].should == "QTI v2.1 Entry Profile Single Section Instance"
question = quiz.quiz_questions.detect{|q| q.position == 2}
question.question_data['question_type'].should == 'multiple_choice_question'
question.assessment_question_id.should == @course.assessment_questions.first.id
end
it "should import VE_TP_02" do
import_fixture('VE_TP_02.zip')
@course.assessment_questions.count.should == 1
@course.quizzes.count.should == 1
quiz = @course.quizzes.first
quiz.quiz_questions.count.should == 2
header = quiz.quiz_questions.detect{|q| q.position == 1}
header.question_data['question_type'].should == 'text_only_question'
header.question_data['question_text'].should == "QTI v2.1 Entry Profile Single Section Instance"
question = quiz.quiz_questions.detect{|q| q.position == 2}
question.question_data['question_type'].should == 'multiple_choice_question'
question.assessment_question_id.should == @course.assessment_questions.first.id
end
it "should import VE_TP_03" do
import_fixture('VE_TP_03.zip')
@course.assessment_questions.count.should == 1
@course.quizzes.count.should == 1
quiz = @course.quizzes.first
quiz.quiz_questions.count.should == 2
header = quiz.quiz_questions.detect{|q| q.position == 1}
header.question_data['question_type'].should == 'text_only_question'
header.question_data['question_text'].should == "QTI v2.1 Entry Profile Single Section Instance"
question = quiz.quiz_questions.detect{|q| q.position == 2}
question.question_data['question_type'].should == 'multiple_answers_question'
question.assessment_question_id.should == @course.assessment_questions.first.id
end
it "should import VE_TP_04" do
import_fixture('VE_TP_04.zip')
@course.assessment_questions.count.should == 1
@course.quizzes.count.should == 1
quiz = @course.quizzes.first
quiz.quiz_questions.count.should == 2
header = quiz.quiz_questions.detect{|q| q.position == 1}
header.question_data['question_type'].should == 'text_only_question'
header.question_data['question_text'].should == "QTI v2.1 Entry Profile Single Section Instance"
question = quiz.quiz_questions.detect{|q| q.position == 2}
question.question_data['question_type'].should == 'fill_in_multiple_blanks_question'
question.assessment_question_id.should == @course.assessment_questions.first.id
end
it "should import VE_TP_05" do
import_fixture('VE_TP_05.zip')
@course.assessment_questions.count.should == 1
@course.quizzes.count.should == 1
quiz = @course.quizzes.first
quiz.quiz_questions.count.should == 2
header = quiz.quiz_questions.detect{|q| q.position == 1}
header.question_data['question_type'].should == 'text_only_question'
header.question_data['question_text'].should == "QTI v2.1 Entry Profile Single Section Instance"
question = quiz.quiz_questions.detect{|q| q.position == 2}
question.question_data['question_type'].should == 'essay_question'
question.assessment_question_id.should == @course.assessment_questions.first.id
end
it "should import VE_TP_06" do
import_fixture('VE_TP_06.zip')
@course.assessment_questions.count.should == 5
@course.quizzes.count.should == 1
quiz = @course.quizzes.first
quiz.quiz_questions.count.should == 6
header = quiz.quiz_questions.detect{|q| q.position == 1}
header.question_data['question_text'].should == "QTI v2.1 Entry Profile Single Section Instance with Multiple Items"
questions = quiz.quiz_questions.sort_by(&:position)
questions.map{|q| q.question_data['question_type']}.should == [
"text_only_question", "multiple_choice_question", "multiple_choice_question",
"multiple_answers_question", "fill_in_multiple_blanks_question", "essay_question"
]
questions.select{|q| q.position > 1}.map(&:assessment_question_id).sort.should == @course.assessment_questions.map(&:id).sort
end
end
end

View File

@ -5,7 +5,7 @@ describe "Converting QTI items" do
file_path = File.join(BASE_FIXTURE_DIR, 'qti')
question = get_question_hash(file_path, 'zero_point_mc')
question[:question_text].should == 'MC - multiple correct with multiple selection. C and D are correct'
question[:question_text].should == "<div class=\"text\"></div>\n<br/>\nMC - multiple correct with multiple selection. C and D are correct"
end
it "should sanitize InstructureMetadata" do