qti 0.3.16 → 0.4.16

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 09a38606bbf7afefc7b808dd338055b24375193b
4
- data.tar.gz: 1dcad5866f62bd26d1dd48ab3f3ab6646de5eddc
3
+ metadata.gz: 4f6c8a03de219970a4b393485157b90247559323
4
+ data.tar.gz: b92d0b520d3b4ed2f18ad89eb25c4b77435c55e1
5
5
  SHA512:
6
- metadata.gz: 952002458866f648ca5c473608a232e46fda37ae6e31cc273d48f25d4449eb4fc8f7f0d020069abb7f4593e72f1a71aaf706841aad90d48a761f0350c4477733
7
- data.tar.gz: b38e9b92eda87771d2b8a9b4bcda1eb052540841b146bcc3c410bc2d67ca1b0ba9f38a2611f5caa579a269aaee55e00216f0b2e79151c38769554b369ed866da
6
+ metadata.gz: f1d379ca58f26bdb0d92db3a3f3929f90774a05104381c9653246a228d9dd3998b78c77b660fb74ae1a049e846bdf2eb8b042d0dcef27ee59781622efcdae95f
7
+ data.tar.gz: 70bba648502519b9d9f6a43c7cdf97dddb141e5359e0e1391ea3db138397a4d0dfd630b593abe6d9a505f48e4bfdb728fc051177848e395f07a5dba6895fdf60
data/lib/qti.rb CHANGED
@@ -65,6 +65,7 @@ require 'qti/v2/models/base'
65
65
  require 'qti/v2/models/interactions/base_interaction'
66
66
  require 'qti/v2/models/choices/simple_choice'
67
67
  require 'qti/v2/models/choices/simple_associable_choice'
68
+ require 'qti/v2/models/choices/gap_match_choice'
68
69
  require 'qti/v2/models/assessment_item'
69
70
  require 'qti/v2/models/assessment_test'
70
71
  require 'qti/v2/models/scoring_data'
@@ -0,0 +1,28 @@
1
+ require 'qti/v2/models/base'
2
+
3
+ module Qti
4
+ module V2
5
+ module Models
6
+ module Choices
7
+ class GapMatchChoice < Qti::V2::Models::Base
8
+ PROHIBITED_NODE_NAMES = 'feedbackInline'.freeze
9
+ def initialize(node)
10
+ @node = node
11
+ end
12
+
13
+ def identifier
14
+ @identifier ||= @node.attributes['identifier'].value
15
+ end
16
+
17
+ def item_body
18
+ @item_body ||= begin
19
+ node = @node.dup
20
+ node.children.filter(PROHIBITED_NODE_NAMES).each(&:unlink)
21
+ node.content.strip.gsub(/\s+/, ' ')
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -2,30 +2,122 @@ module Qti
2
2
  module V2
3
3
  module Models
4
4
  module Interactions
5
- class GapMatchInteraction < Qti::V2::Models::Base
6
- NODE_NAME = 'gapMatchInteraction'.freeze
7
- # This will know if a class matches
5
+ class GapMatchInteraction < BaseInteraction
8
6
  def self.matches(node)
9
- matches = node.children.filter(INTERACTION_ELEMENTS_CSS).select { |n| n.name == NODE_NAME }
10
- return false if matches.empty?
11
-
12
- raise Qti::UnsupportedSchema if matches.size > 1
13
- new(matches.first)
7
+ matches = node.xpath('.//xmlns:gapMatchInteraction')
8
+ return false if matches.count != 1
9
+ new(node)
14
10
  end
15
11
 
16
12
  def initialize(node)
17
13
  @node = node
18
14
  end
19
15
 
20
- def gap_max_count
21
- @node.attributes['gapMax']&.value&.to_i
16
+ def shuffled?
17
+ @node.at_xpath('.//xmlns:gapMatchInteraction').attributes['shuffle']&.value.try(:downcase) == 'true'
18
+ end
19
+
20
+ def clean_stem_items
21
+ @clean_stem_items ||= begin
22
+ node = @node.dup
23
+ gap_node = node.xpath('.//xmlns:gapMatchInteraction')
24
+ gap_node.children.filter('gapText').each(&:remove)
25
+
26
+ gap_node.children.each do |child|
27
+ if child.inner_text.strip.empty?
28
+ child.remove
29
+ end
30
+ end
31
+
32
+ gap_node
33
+ end
34
+ end
35
+
36
+ def prompt
37
+ clean_stem_items.children.filter('prompt')
38
+ end
39
+
40
+ def stem_items
41
+ if prompt.present?
42
+ stem_text_with_prompt = stem_text.unshift(prompt_hash)
43
+ stem_items_with_id_and_position(stem_text_with_prompt)
44
+ else
45
+ stem_items_with_id_and_position(stem_text)
46
+ end
47
+ end
48
+
49
+ def stem_items_with_id_and_position(stem_text)
50
+ stem_text.map.with_index do |stem_item, index|
51
+ stem_item.merge(
52
+ id: "stem_#{index}",
53
+ position: index + 1
54
+ )
55
+ end
22
56
  end
23
57
 
24
- def gap_min_count
25
- @node.attributes['gapMin']&.value&.to_i
58
+ def prompt_hash
59
+ {
60
+ type: 'text',
61
+ value: prompt.first.text
62
+ }
26
63
  end
27
64
 
28
- def gap_choice
65
+ def stem_text
66
+ clean_stem_items.search('p').children.map do |stem_item|
67
+ if stem_item.name == 'gap'
68
+ {
69
+ type: 'blank',
70
+ blank_id: stem_item.attributes['identifier'].value
71
+ }
72
+ else
73
+ {
74
+ type: 'text',
75
+ value: stem_item.text.empty? ? " " : stem_item.text
76
+ }
77
+ end
78
+ end
79
+ end
80
+
81
+ def blanks
82
+ node.xpath('.//xmlns:gapText').map do |blank|
83
+ { id: blank.attributes['identifier'].value }
84
+ end
85
+ end
86
+
87
+ def answers
88
+ @answers ||= answer_nodes.map do |node|
89
+ V2::Models::Choices::GapMatchChoice.new(node)
90
+ end
91
+ end
92
+
93
+ def scoring_data_structs
94
+ question_response_pairs = node.xpath('.//xmlns:correctResponse//xmlns:value').map do |value|
95
+ value.content.split
96
+ end
97
+ question_response_pairs.map!{ |qrp| qrp.reverse }
98
+ question_response_id_mapping = Hash[question_response_pairs]
99
+ answer_nodes.map { |value_node|
100
+ node_id = value_node.attributes['identifier']&.value
101
+ answer_choice = choices.find{ |choice| choice.attributes['identifier']&.value == question_response_id_mapping[node_id] }
102
+ ScoringData.new(
103
+ answer_choice.content,
104
+ 'directedPair',
105
+ {
106
+ id: node_id,
107
+ case: false
108
+ }
109
+ )
110
+ }
111
+ end
112
+
113
+ def choices
114
+ @node.xpath('.//xmlns:gapText')
115
+ end
116
+
117
+ private
118
+
119
+ def answer_nodes
120
+ @node.xpath('.//xmlns:gap')
29
121
  end
30
122
  end
31
123
  end
@@ -1,7 +1,11 @@
1
1
  module Qti
2
2
  module V2
3
3
  module Models
4
- ScoringData = Struct.new(:values, :type)
4
+ ScoringData = Struct.new(:values, :type, :id, :case) do
5
+ def initialize(values, rcardinality, options={})
6
+ super(values, rcardinality, options[:id], options[:case])
7
+ end
8
+ end
5
9
  end
6
10
  end
7
11
  end
@@ -1,33 +1,33 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p2"
3
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
- xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2.xsd"
5
- identifier="gapMatch" title="Richard III (Take 1)" adaptive="false" timeDependent="false">
6
- <responseDeclaration identifier="RESPONSE" cardinality="multiple" baseType="directedPair">
7
- <correctResponse>
8
- <value>W G1</value>
9
- <value>Su G2</value>
10
- </correctResponse>
11
- <mapping defaultValue="-1" lowerBound="0">
12
- <mapEntry mapKey="W G1" mappedValue="1"/>
13
- <mapEntry mapKey="Su G2" mappedValue="2"/>
14
- </mapping>
15
- </responseDeclaration>
16
- <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
17
- <itemBody>
18
- <gapMatchInteraction responseIdentifier="RESPONSE" shuffle="false">
19
- <prompt>Identify the missing words in this famous quote from Shakespeare's Richard III.</prompt>
20
- <gapText identifier="W" matchMax="1">winter</gapText>
21
- <gapText identifier="Sp" matchMax="1">spring</gapText>
22
- <gapText identifier="Su" matchMax="1">summer</gapText>
23
- <gapText identifier="A" matchMax="1">autumn</gapText>
24
- <blockquote>
25
- <p>Now is the <gap identifier="G1"/> of our discontent<br/> Made glorious <gap
26
- identifier="G2"/> by this sun of York;<br/> And all the clouds that lour'd
27
- upon our house<br/> In the deep bosom of the ocean buried.</p>
28
- </blockquote>
29
- </gapMatchInteraction>
30
- </itemBody>
31
- <responseProcessing
32
- template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/map_response"/>
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2.xsd"
5
+ identifier="gapMatch" title="Richard III (Take 1)" adaptive="false" timeDependent="false">
6
+ <responseDeclaration identifier="RESPONSE" cardinality="multiple" baseType="directedPair">
7
+ <correctResponse>
8
+ <value>W G1</value>
9
+ <value>Su G2</value>
10
+ </correctResponse>
11
+ <mapping defaultValue="-1" lowerBound="0">
12
+ <mapEntry mapKey="W G1" mappedValue="1"/>
13
+ <mapEntry mapKey="Su G2" mappedValue="2"/>
14
+ </mapping>
15
+ </responseDeclaration>
16
+ <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
17
+ <itemBody>
18
+ <gapMatchInteraction responseIdentifier="RESPONSE" shuffle="false">
19
+ <prompt>Identify the missing words in this famous quote from Shakespeare's Richard III.</prompt>
20
+ <gapText identifier="W" matchMax="1">winter</gapText>
21
+ <gapText identifier="Sp" matchMax="1">spring</gapText>
22
+ <gapText identifier="Su" matchMax="1">summer</gapText>
23
+ <gapText identifier="A" matchMax="1">autumn</gapText>
24
+ <blockquote>
25
+ <p>Now is the <gap identifier="G1"/> of our discontent<br/> Made glorious <gap
26
+ identifier="G2"/> by this sun of York;<br/> And all the clouds that lour'd
27
+ upon our house<br/> In the deep bosom of the ocean buried.</p>
28
+ </blockquote>
29
+ </gapMatchInteraction>
30
+ </itemBody>
31
+ <responseProcessing
32
+ template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/map_response"/>
33
33
  </assessmentItem>
@@ -1,27 +1,27 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p2"
3
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
- xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2.xsd"
5
- identifier="textEntry" title="Richard III (Take 3)" adaptive="false" timeDependent="false">
6
- <responseDeclaration identifier="RESPONSE" cardinality="single" baseType="string">
7
- <correctResponse>
8
- <value>York</value>
9
- </correctResponse>
10
- <mapping defaultValue="0">
11
- <mapEntry mapKey="York" mappedValue="1"/>
12
- <mapEntry mapKey="york" mappedValue="0.5"/>
13
- </mapping>
14
- </responseDeclaration>
15
- <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
16
- <itemBody>
17
- <p>Identify the missing word in this famous quote from Shakespeare's Richard III.</p>
18
- <blockquote>
19
- <p>Now is the winter of our discontent<br/> Made glorious summer by this sun of
20
- <textEntryInteraction responseIdentifier="RESPONSE" expectedLength="15"/>;<br/>
21
- And all the clouds that lour'd upon our house<br/> In the deep bosom of the ocean
22
- buried.</p>
23
- </blockquote>
24
- </itemBody>
25
- <responseProcessing
26
- template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/map_response"/>
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2.xsd"
5
+ identifier="textEntry" title="Richard III (Take 3)" adaptive="false" timeDependent="false">
6
+ <responseDeclaration identifier="RESPONSE" cardinality="single" baseType="string">
7
+ <correctResponse>
8
+ <value>York</value>
9
+ </correctResponse>
10
+ <mapping defaultValue="0">
11
+ <mapEntry mapKey="York" mappedValue="1"/>
12
+ <mapEntry mapKey="york" mappedValue="0.5"/>
13
+ </mapping>
14
+ </responseDeclaration>
15
+ <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float"/>
16
+ <itemBody>
17
+ <p>Identify the missing word in this famous quote from Shakespeare's Richard III.</p>
18
+ <blockquote>
19
+ <p>Now is the winter of our discontent<br/> Made glorious summer by this sun of
20
+ <textEntryInteraction responseIdentifier="RESPONSE" expectedLength="15"/>;<br/>
21
+ And all the clouds that lour'd upon our house<br/> In the deep bosom of the ocean
22
+ buried.</p>
23
+ </blockquote>
24
+ </itemBody>
25
+ <responseProcessing
26
+ template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/map_response"/>
27
27
  </assessmentItem>
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p2"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p2 http://www.imsglobal.org/xsd/qti/qtiv2p2/imsqti_v2p2.xsd"
5
+ identifier="gapMatch" title="Richard III (Take 1)" adaptive="false" timeDependent="false">
6
+ <responseDeclaration identifier="RESPONSE" cardinality="multiple" baseType="directedPair">
7
+ <correctResponse>
8
+ <value>W G1</value>
9
+ <value>Su G2</value>
10
+ </correctResponse>
11
+ <mapping defaultValue="-1" lowerBound="0">
12
+ <mapEntry mapKey="W G1" mappedValue="1"/>
13
+ <mapEntry mapKey="Su G2" mappedValue="2"/>
14
+ </mapping>
15
+ </responseDeclaration>
16
+ <outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float" normalMaximum="2.0"/>
17
+ <itemBody>
18
+ <gapMatchInteraction responseIdentifier="RESPONSE" shuffle="false">
19
+ <prompt>Identify the missing words in this famous quote from Shakespeare's Richard III.</prompt>
20
+ <gapText identifier="W" matchMax="1">winter</gapText>
21
+ <gapText identifier="Sp" matchMax="1">spring</gapText>
22
+ <gapText identifier="Su" matchMax="1">summer</gapText>
23
+ <gapText identifier="A" matchMax="1">autumn</gapText>
24
+ <blockquote>
25
+ <p>Now is the <gap identifier="G1"/> of our discontent<br/> Made glorious <gap
26
+ identifier="G2"/> by this sun of York;<br/> And all the clouds that lour'd
27
+ upon our house<br/> In the deep bosom of the ocean buried.</p>
28
+ </blockquote>
29
+ </gapMatchInteraction>
30
+ </itemBody>
31
+ <responseProcessing
32
+ template="http://www.imsglobal.org/question/qti_v2p2/rptemplates/map_response"/>
33
+ </assessmentItem>
@@ -155,6 +155,7 @@
155
155
  <file href="multiple-choice/multiple-choice.xml"/>
156
156
  <file href="text-entry/text-entry.xml"/>
157
157
  <file href="true-false/true-false.xml"/>
158
+ <file href="fib/fib.xml"/>
158
159
  <file href="images/sign.png"/>
159
160
  </resource>
160
161
  </resources>
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qti::V2::Models::Choices::GapMatchChoice do
4
+ context 'choice.xml' do
5
+ let(:io) { File.read(File.join('spec', 'fixtures', 'items_2.1', 'gap_match.xml')) }
6
+ let(:nodes) { Nokogiri::XML(io).xpath('//xmlns:gapText') }
7
+
8
+ let(:choices) { nodes.map { |node| described_class.new(node) } }
9
+
10
+ it 'returns the right identifier' do
11
+ expect(choices.map(&:identifier)).to eq %w(W Sp Su A)
12
+ end
13
+
14
+ it 'returns the item body text' do
15
+ expect(choices.map(&:item_body).map(&:empty?).any?).to eq false
16
+ end
17
+
18
+ it 'removes inline feedback elements from item_body' do
19
+ expect(choices.map(&:item_body).map { |text| text.include? 'correct' }.any?).to eq false
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe Qti::V2::Models::Interactions::GapMatchInteraction do
4
+ context 'gap_match.xml' do
5
+ let(:io) { File.read(File.join('spec', 'fixtures', 'items_2.1', 'gap_match.xml')) }
6
+ let(:node) { Nokogiri::XML(io) }
7
+ let(:loaded_class) { described_class.new(node) }
8
+
9
+ let(:expected_stem_items) {[
10
+ {type: "text",
11
+ value: "Identify the missing words in this famous quote from Shakespeare's Richard III.",
12
+ id: "stem_0", position: 1},
13
+ {type: 'text', value: 'Now is the ', id: 'stem_1', position: 2},
14
+ {type: 'blank', :blank_id=>'G1', id: 'stem_2', position: 3},
15
+ {type: 'text', value: ' of our discontent', id: 'stem_3', position: 4},
16
+ {type: 'text', value: ' ', id: 'stem_4', position: 5},
17
+ {type: 'text', value: ' Made glorious ', id: 'stem_5', position: 6},
18
+ {type: 'blank', :blank_id=>'G2', id: 'stem_6', position: 7},
19
+ {type: 'text', value: ' by this sun of York;', id: 'stem_7', position: 8},
20
+ {type: 'text', value: ' ', id: 'stem_8', position: 9},
21
+ {type: 'text', value: " And all the clouds that lour'd\n upon our house",
22
+ id: 'stem_9', position: 10},
23
+ {type: 'text', value: ' ', id: 'stem_10', position: 11},
24
+ {type: 'text', value: ' In the deep bosom of the ocean buried.',
25
+ id: 'stem_11', position: 12}
26
+ ]}
27
+
28
+ let(:scoring_data_ids) { %w(G1 G2) }
29
+ let(:scoring_data_values) { %w(winter summer) }
30
+
31
+ it 'returns shuffle setting' do
32
+ expect(loaded_class.shuffled?).to eq false
33
+ end
34
+
35
+ it 'returns the blanks' do
36
+ expect(loaded_class.blanks).to eq [{ id: "W" }, { id: "Sp" }, { id: "Su" }, { id: "A" }]
37
+ end
38
+
39
+ it 'returns the stem_items' do
40
+ expect(loaded_class.stem_items).to eq(expected_stem_items)
41
+ end
42
+
43
+ it 'returns the answers' do
44
+ expect(loaded_class.answers.count).to eq 2
45
+ expect(loaded_class.answers.first).to be_an_instance_of(Qti::V2::Models::Choices::GapMatchChoice)
46
+ end
47
+
48
+
49
+ it 'returns the choices' do
50
+ expect(loaded_class.choices.count).to eq 4
51
+ end
52
+
53
+ it 'returns the scoring_data_structs' do
54
+ expect(loaded_class.scoring_data_structs.map(&:id)).to eq(scoring_data_ids)
55
+ expect(loaded_class.scoring_data_structs.map(&:values)).to eq(scoring_data_values)
56
+ end
57
+ end
58
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qti
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.16
4
+ version: 0.4.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hannah Bottalla
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-05-03 00:00:00.000000000 Z
12
+ date: 2017-05-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -350,6 +350,7 @@ files:
350
350
  - spec/fixtures/test_qti_2.2.zip
351
351
  - spec/fixtures/test_qti_2.2/assessment.xml
352
352
  - spec/fixtures/test_qti_2.2/essay/essay.xml
353
+ - spec/fixtures/test_qti_2.2/fib/fib.xml
353
354
  - spec/fixtures/test_qti_2.2/images/sign.png
354
355
  - spec/fixtures/test_qti_2.2/imsmanifest.xml
355
356
  - spec/fixtures/test_qti_2.2/mathassess.xsd
@@ -424,9 +425,11 @@ files:
424
425
  - spec/lib/qti/v1/models/interactions/string_interaction_spec.rb
425
426
  - spec/lib/qti/v2/models/assessment_item_spec.rb
426
427
  - spec/lib/qti/v2/models/assessment_test_spec.rb
428
+ - spec/lib/qti/v2/models/choices/gap_match_choice_spec.rb
427
429
  - spec/lib/qti/v2/models/choices/simple_choice_spec.rb
428
430
  - spec/lib/qti/v2/models/interactions/choice_interaction_spec.rb
429
431
  - spec/lib/qti/v2/models/interactions/extended_text_interaction_spec.rb
432
+ - spec/lib/qti/v2/models/interactions/gap_match_interaction_spec.rb
430
433
  - spec/lib/qti/v2/models/interactions/match_interaction_spec.rb
431
434
  - spec/lib/qti_spec.rb
432
435
  - spec/spec_helper.rb
@@ -577,6 +580,7 @@ test_files:
577
580
  - spec/fixtures/test_qti_2.2.zip
578
581
  - spec/fixtures/test_qti_2.2/assessment.xml
579
582
  - spec/fixtures/test_qti_2.2/essay/essay.xml
583
+ - spec/fixtures/test_qti_2.2/fib/fib.xml
580
584
  - spec/fixtures/test_qti_2.2/images/sign.png
581
585
  - spec/fixtures/test_qti_2.2/imsmanifest.xml
582
586
  - spec/fixtures/test_qti_2.2/mathassess.xsd
@@ -651,9 +655,11 @@ test_files:
651
655
  - spec/lib/qti/v1/models/interactions/string_interaction_spec.rb
652
656
  - spec/lib/qti/v2/models/assessment_item_spec.rb
653
657
  - spec/lib/qti/v2/models/assessment_test_spec.rb
658
+ - spec/lib/qti/v2/models/choices/gap_match_choice_spec.rb
654
659
  - spec/lib/qti/v2/models/choices/simple_choice_spec.rb
655
660
  - spec/lib/qti/v2/models/interactions/choice_interaction_spec.rb
656
661
  - spec/lib/qti/v2/models/interactions/extended_text_interaction_spec.rb
662
+ - spec/lib/qti/v2/models/interactions/gap_match_interaction_spec.rb
657
663
  - spec/lib/qti/v2/models/interactions/match_interaction_spec.rb
658
664
  - spec/lib/qti_spec.rb
659
665
  - spec/spec_helper.rb