@conform-ed/contracts 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -2
- package/.turbo/turbo-format.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +178 -173
- package/package.json +1 -1
- package/src/qti/v3_0_1/assessment-internal.ts +41 -19
- package/src/qti/v3_0_1/processing-internal.ts +12 -1
- package/test/qti-v3_0_1.test.ts +108 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
[0m[2m[35m$[0m [2m[1mtsgo --noEmit[0m
|
|
1
|
+
$ tsgo --noEmit
|
package/.turbo/turbo-format.log
CHANGED
package/.turbo/turbo-lint.log
CHANGED
package/.turbo/turbo-test.log
CHANGED
|
@@ -2,222 +2,227 @@ $ bun test
|
|
|
2
2
|
bun test v1.3.14 (0d9b296a)
|
|
3
3
|
|
|
4
4
|
test/xapi.test.ts:
|
|
5
|
-
(pass) xAPI 1.0.3 statement schema accepts the core data model [
|
|
6
|
-
(pass) xAPI 2.0 statement schema accepts the same core data model [
|
|
7
|
-
(pass) xAPI 2.0 statement schema accepts IEEE 2.0 contextAgent/contextGroup objects [
|
|
8
|
-
(pass) xAPI statement submission schema accepts batches [0.
|
|
9
|
-
(pass) xAPI document, listing, and about schemas validate LRS metadata [
|
|
10
|
-
(pass) xAPI validation rejects statements missing required fields [0.
|
|
11
|
-
(pass) xAPI statement result, person object, and resource queries validate missing spec objects [
|
|
12
|
-
(pass) xAPI Agent schema enforces exactly one Inverse Functional Identifier [0.
|
|
13
|
-
(pass) xAPI HTTP method enum validates LRS operations [0.
|
|
14
|
-
(pass) xAPI error response schema validates LRS error payloads [0.
|
|
15
|
-
(pass) xAPI error codes validate standard HTTP status responses [0.
|
|
16
|
-
(pass) xAPI concurrency schema validates ETags and conditional headers [0.
|
|
17
|
-
(pass) xAPI multipart attachment part schema validates attachment data structure [0.
|
|
18
|
-
(pass) xAPI multipart request schema validates statement with attachments [0.
|
|
19
|
-
(pass) xAPI resource schema models LRS endpoint definitions [
|
|
20
|
-
(pass) xAPI request and response headers are properly enumerated [0.
|
|
5
|
+
(pass) xAPI 1.0.3 statement schema accepts the core data model [35.92ms]
|
|
6
|
+
(pass) xAPI 2.0 statement schema accepts the same core data model [2.06ms]
|
|
7
|
+
(pass) xAPI 2.0 statement schema accepts IEEE 2.0 contextAgent/contextGroup objects [10.31ms]
|
|
8
|
+
(pass) xAPI statement submission schema accepts batches [0.46ms]
|
|
9
|
+
(pass) xAPI document, listing, and about schemas validate LRS metadata [2.80ms]
|
|
10
|
+
(pass) xAPI validation rejects statements missing required fields [0.59ms]
|
|
11
|
+
(pass) xAPI statement result, person object, and resource queries validate missing spec objects [8.00ms]
|
|
12
|
+
(pass) xAPI Agent schema enforces exactly one Inverse Functional Identifier [0.50ms]
|
|
13
|
+
(pass) xAPI HTTP method enum validates LRS operations [0.16ms]
|
|
14
|
+
(pass) xAPI error response schema validates LRS error payloads [0.73ms]
|
|
15
|
+
(pass) xAPI error codes validate standard HTTP status responses [0.28ms]
|
|
16
|
+
(pass) xAPI concurrency schema validates ETags and conditional headers [0.43ms]
|
|
17
|
+
(pass) xAPI multipart attachment part schema validates attachment data structure [0.19ms]
|
|
18
|
+
(pass) xAPI multipart request schema validates statement with attachments [0.60ms]
|
|
19
|
+
(pass) xAPI resource schema models LRS endpoint definitions [1.01ms]
|
|
20
|
+
(pass) xAPI request and response headers are properly enumerated [0.19ms]
|
|
21
21
|
|
|
22
22
|
test/cmi5-v1_0.test.ts:
|
|
23
|
-
(pass) cmi5 course structure schema parses the simple XML example [
|
|
24
|
-
(pass) cmi5 keyword extension schema parses the extended XML example [
|
|
25
|
-
(pass) cmi5 recursive course structure schema accepts nested blocks and AUs [
|
|
23
|
+
(pass) cmi5 course structure schema parses the simple XML example [18.53ms]
|
|
24
|
+
(pass) cmi5 keyword extension schema parses the extended XML example [20.14ms]
|
|
25
|
+
(pass) cmi5 recursive course structure schema accepts nested blocks and AUs [1.39ms]
|
|
26
26
|
|
|
27
27
|
test/oneroster-v1_2.test.ts:
|
|
28
|
-
(pass) UserSchema parses a minimal OneRoster user [
|
|
29
|
-
(pass) LineItemSchema parses a minimal gradebook line item [1.
|
|
30
|
-
(pass) ResourceSchema parses a minimal resource entry [
|
|
31
|
-
(pass) ImsxStatusInfoSchema parses a OneRoster status envelope [
|
|
32
|
-
(pass) EnrollmentRoleSchema accepts extension values and rejects unknown built-ins [0.
|
|
33
|
-
(pass) ClassSchema requires at least one term reference [
|
|
28
|
+
(pass) UserSchema parses a minimal OneRoster user [8.15ms]
|
|
29
|
+
(pass) LineItemSchema parses a minimal gradebook line item [1.87ms]
|
|
30
|
+
(pass) ResourceSchema parses a minimal resource entry [3.70ms]
|
|
31
|
+
(pass) ImsxStatusInfoSchema parses a OneRoster status envelope [1.55ms]
|
|
32
|
+
(pass) EnrollmentRoleSchema accepts extension values and rejects unknown built-ins [0.71ms]
|
|
33
|
+
(pass) ClassSchema requires at least one term reference [2.44ms]
|
|
34
34
|
(pass) OneRoster12DerivedZodTemplates exposes core published entry points [0.05ms]
|
|
35
|
-
(pass) OneRoster REST binding operation catalogs expose all endpoint schemas [0.
|
|
36
|
-
(pass) Gradebook operation bindings expose request/response payload schemas [0.
|
|
37
|
-
(pass) ManifestCsvRowSchema accepts either starred or unstarred optional file keys [
|
|
38
|
-
(pass) UsersCsvRowSchema validates required OneRoster 1.2 user CSV columns [1.
|
|
39
|
-
(pass) OneRosterCsvBindingPackageSchema validates package-level CSV document sets [
|
|
35
|
+
(pass) OneRoster REST binding operation catalogs expose all endpoint schemas [0.04ms]
|
|
36
|
+
(pass) Gradebook operation bindings expose request/response payload schemas [0.10ms]
|
|
37
|
+
(pass) ManifestCsvRowSchema accepts either starred or unstarred optional file keys [2.17ms]
|
|
38
|
+
(pass) UsersCsvRowSchema validates required OneRoster 1.2 user CSV columns [1.42ms]
|
|
39
|
+
(pass) OneRosterCsvBindingPackageSchema validates package-level CSV document sets [2.40ms]
|
|
40
40
|
|
|
41
41
|
test/qti-v2_1.test.ts:
|
|
42
|
-
(pass) QtiV2_1 metadata document parses a minimal metadata payload [
|
|
43
|
-
(pass) QtiV2_1 assessment item document parses a minimal choice item [
|
|
44
|
-
(pass) QtiV2_1 result document rejects record responses without field identifiers [
|
|
45
|
-
(pass) QtiV2_1 manifest document parses a minimal package [
|
|
46
|
-
(pass) QtiV2_1 APIP accessibility document parses a minimal payload [
|
|
42
|
+
(pass) QtiV2_1 metadata document parses a minimal metadata payload [1.39ms]
|
|
43
|
+
(pass) QtiV2_1 assessment item document parses a minimal choice item [18.91ms]
|
|
44
|
+
(pass) QtiV2_1 result document rejects record responses without field identifiers [2.78ms]
|
|
45
|
+
(pass) QtiV2_1 manifest document parses a minimal package [2.33ms]
|
|
46
|
+
(pass) QtiV2_1 APIP accessibility document parses a minimal payload [2.54ms]
|
|
47
47
|
|
|
48
48
|
test/qti-v3_0_1.test.ts:
|
|
49
|
-
(pass) QtiMetadataDocumentSchema parses a minimal metadata document [
|
|
50
|
-
(pass) QtiAccessForAllPnpDocumentSchema parses a minimal preferences document [
|
|
51
|
-
(pass) QtiAssessmentResultDocumentSchema parses a minimal result report [
|
|
52
|
-
(pass) QtiAssessmentResultDocumentSchema rejects record response values without field identifiers [0.
|
|
53
|
-
(pass) QtiAssessmentResultDocumentSchema rejects record outcomes with baseType [0.
|
|
54
|
-
(pass) QtiUsageDataDocumentSchema parses ordinary and categorized statistics [
|
|
55
|
-
(pass) QtiUsageDataDocumentSchema rejects invalid objectType and mapping bounds [0.
|
|
56
|
-
(pass) QtiResponseProcessingDocumentSchema parses typed processing operators [
|
|
57
|
-
(pass) QtiOutcomeProcessingDocumentSchema parses typed aggregate operators [
|
|
58
|
-
(pass) QtiResponseProcessingDocumentSchema rejects invalid operator parameters [
|
|
59
|
-
(pass) QtiOutcomeProcessingDocumentSchema rejects invalid operator arity and ranges [
|
|
60
|
-
(pass) QtiAssessmentSectionDocumentSchema parses a minimal standalone section [
|
|
61
|
-
(pass) QtiAssessmentStimulusDocumentSchema parses a minimal stimulus [
|
|
62
|
-
(pass) QtiOutcomeDeclarationDocumentSchema parses a minimal outcome declaration [
|
|
63
|
-
(pass) QtiAsiProfileDocumentSchema accepts resource-specific QTI documents [1.
|
|
64
|
-
(pass) QtiAssessmentItemDocumentSchema parses a minimal choice item [
|
|
65
|
-
(pass) QtiAssessmentItemDocumentSchema rejects incompatible response declarations [
|
|
66
|
-
(pass) QtiAssessmentTestDocumentSchema parses a minimal test document [
|
|
67
|
-
(pass) Qti301DerivedZodTemplates exposes expected document templates [0.
|
|
68
|
-
(pass) QTI barrel exports prefixed XML extension node helpers [0.
|
|
49
|
+
(pass) QtiMetadataDocumentSchema parses a minimal metadata document [1.33ms]
|
|
50
|
+
(pass) QtiAccessForAllPnpDocumentSchema parses a minimal preferences document [18.97ms]
|
|
51
|
+
(pass) QtiAssessmentResultDocumentSchema parses a minimal result report [4.39ms]
|
|
52
|
+
(pass) QtiAssessmentResultDocumentSchema rejects record response values without field identifiers [0.50ms]
|
|
53
|
+
(pass) QtiAssessmentResultDocumentSchema rejects record outcomes with baseType [0.90ms]
|
|
54
|
+
(pass) QtiUsageDataDocumentSchema parses ordinary and categorized statistics [6.82ms]
|
|
55
|
+
(pass) QtiUsageDataDocumentSchema rejects invalid objectType and mapping bounds [0.61ms]
|
|
56
|
+
(pass) QtiResponseProcessingDocumentSchema parses typed processing operators [19.36ms]
|
|
57
|
+
(pass) QtiOutcomeProcessingDocumentSchema parses typed aggregate operators [16.35ms]
|
|
58
|
+
(pass) QtiResponseProcessingDocumentSchema rejects invalid operator parameters [4.38ms]
|
|
59
|
+
(pass) QtiOutcomeProcessingDocumentSchema rejects invalid operator arity and ranges [6.65ms]
|
|
60
|
+
(pass) QtiAssessmentSectionDocumentSchema parses a minimal standalone section [4.22ms]
|
|
61
|
+
(pass) QtiAssessmentStimulusDocumentSchema parses a minimal stimulus [2.99ms]
|
|
62
|
+
(pass) QtiOutcomeDeclarationDocumentSchema parses a minimal outcome declaration [1.54ms]
|
|
63
|
+
(pass) QtiAsiProfileDocumentSchema accepts resource-specific QTI documents [1.99ms]
|
|
64
|
+
(pass) QtiAssessmentItemDocumentSchema parses a minimal choice item [43.00ms]
|
|
65
|
+
(pass) QtiAssessmentItemDocumentSchema rejects incompatible response declarations [1.16ms]
|
|
66
|
+
(pass) QtiAssessmentTestDocumentSchema parses a minimal test document [3.47ms]
|
|
67
|
+
(pass) Qti301DerivedZodTemplates exposes expected document templates [0.04ms]
|
|
68
|
+
(pass) QTI barrel exports prefixed XML extension node helpers [0.41ms]
|
|
69
|
+
(pass) built-in outcome completionStatus needs no declaration in processing rules [0.30ms]
|
|
70
|
+
(pass) an integer SCORE is accepted — the official corpus ships it [0.17ms]
|
|
71
|
+
(pass) a non-numeric or multiple SCORE is still rejected [0.18ms]
|
|
72
|
+
(pass) snake_case completion_status is accepted as the built-in's corpus alias [0.23ms]
|
|
73
|
+
(pass) maxChoices 0 means unbounded and never conflicts with minChoices [0.68ms]
|
|
69
74
|
|
|
70
75
|
test/contracts.test.ts:
|
|
71
|
-
(pass) RunnerConfigSchema parses minimal config [
|
|
72
|
-
(pass) RunnerConfigSchema requires adapter for cmi5 suite [0.
|
|
73
|
-
(pass) RunnerConfigSchema enforces bearer adapter token source [0.
|
|
74
|
-
(pass) AdapterCapabilitySchema validates profile payload [0.
|
|
75
|
-
(pass) AdapterProfileSchema validates cmi5 interoperability contract [1.
|
|
76
|
-
(pass) AdapterProfileSchema requires cmi5 packageUpload [0.
|
|
77
|
-
(pass) AdapterErrorSchema validates uniform error envelope [0.
|
|
76
|
+
(pass) RunnerConfigSchema parses minimal config [1.64ms]
|
|
77
|
+
(pass) RunnerConfigSchema requires adapter for cmi5 suite [0.15ms]
|
|
78
|
+
(pass) RunnerConfigSchema enforces bearer adapter token source [0.72ms]
|
|
79
|
+
(pass) AdapterCapabilitySchema validates profile payload [0.65ms]
|
|
80
|
+
(pass) AdapterProfileSchema validates cmi5 interoperability contract [1.30ms]
|
|
81
|
+
(pass) AdapterProfileSchema requires cmi5 packageUpload [0.10ms]
|
|
82
|
+
(pass) AdapterErrorSchema validates uniform error envelope [0.48ms]
|
|
78
83
|
|
|
79
84
|
test/qti-v2_2.test.ts:
|
|
80
|
-
(pass) QtiV2_2 metadata document parses a minimal metadata payload with scoring modes [0.
|
|
81
|
-
(pass) QtiV2_2 assessment stimulus and assessment item documents parse together [
|
|
82
|
-
(pass) QtiV2_2 processing rejects invalid numeric operator parameters [
|
|
83
|
-
(pass) QtiV2_2 curriculum standards metadata and manifest documents parse [
|
|
84
|
-
(pass) QtiV2_2 APIP accessibility document parses external supplemental accessibility [
|
|
85
|
+
(pass) QtiV2_2 metadata document parses a minimal metadata payload with scoring modes [0.82ms]
|
|
86
|
+
(pass) QtiV2_2 assessment stimulus and assessment item documents parse together [6.18ms]
|
|
87
|
+
(pass) QtiV2_2 processing rejects invalid numeric operator parameters [11.22ms]
|
|
88
|
+
(pass) QtiV2_2 curriculum standards metadata and manifest documents parse [2.40ms]
|
|
89
|
+
(pass) QtiV2_2 APIP accessibility document parses external supplemental accessibility [2.51ms]
|
|
85
90
|
|
|
86
91
|
test/cat-v1_0.test.ts:
|
|
87
|
-
(pass) CAT v1.0 Zod Schemas > Shared validators > UuidSchema validates RFC4122 compliant UUIDs [
|
|
88
|
-
(pass) CAT v1.0 Zod Schemas > Shared validators > DateTimeSchema validates ISO8601 datetimes [0.
|
|
89
|
-
(pass) CAT v1.0 Zod Schemas > Shared validators > ExtensionEnum supports standard values and ext:* vendor extensions [0.
|
|
90
|
-
(pass) CAT v1.0 Zod Schemas > Shared validators > OutcomeVariableType supports outcome types and vendor extensions [0.
|
|
91
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable validates outcome variable structure [0.
|
|
92
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable rejects additional properties [
|
|
93
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > ResponseVariable validates candidate responses [0.
|
|
94
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > ItemRef validates item references [0.
|
|
95
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > ItemPool validates item pool with multiple items [0.
|
|
96
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > SectionData validates complete section configuration [0.
|
|
97
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > ItemStage validates items to present to candidate [0.
|
|
98
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > AssessmentResult validates candidate responses and outcomes [
|
|
99
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > CatEngineResultReport validates CAT engine response [0.
|
|
100
|
-
(pass) CAT v1.0 Zod Schemas > Core data types > SessionInfo validates session state [0.
|
|
101
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionRequest validates section creation [0.
|
|
102
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionResponse validates section creation response [0.
|
|
103
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionRequest validates session creation [0.
|
|
104
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionResponse validates session creation response [0.
|
|
105
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsRequest validates result submission [0.
|
|
106
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsResponse validates CAT engine result report [0.
|
|
107
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSessionRequest validates session termination [0.
|
|
108
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSessionResponse validates session termination response [0.
|
|
109
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionRequest validates section closure [0.
|
|
110
|
-
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionResponse validates section closure response [0.
|
|
111
|
-
(pass) CAT v1.0 Zod Schemas > REST operation bindings > REST operation catalog exposes all 6 CAT operations [0.
|
|
112
|
-
(pass) CAT v1.0 Zod Schemas > REST operation bindings > CreateSection operation has correct structure [0.
|
|
113
|
-
(pass) CAT v1.0 Zod Schemas > REST operation bindings > SubmitResults operation has correct path template [0.
|
|
114
|
-
(pass) CAT v1.0 Zod Schemas > Derived templates > Cat10DerivedZodTemplates exposes specification metadata [0.
|
|
115
|
-
(pass) CAT v1.0 Zod Schemas > Derived templates > Specification links point to official CAT v1.0 resources [0.
|
|
116
|
-
(pass) CAT v1.0 Zod Schemas > Error handling > ErrorResponse validates error payloads [0.
|
|
92
|
+
(pass) CAT v1.0 Zod Schemas > Shared validators > UuidSchema validates RFC4122 compliant UUIDs [0.76ms]
|
|
93
|
+
(pass) CAT v1.0 Zod Schemas > Shared validators > DateTimeSchema validates ISO8601 datetimes [0.29ms]
|
|
94
|
+
(pass) CAT v1.0 Zod Schemas > Shared validators > ExtensionEnum supports standard values and ext:* vendor extensions [0.40ms]
|
|
95
|
+
(pass) CAT v1.0 Zod Schemas > Shared validators > OutcomeVariableType supports outcome types and vendor extensions [0.07ms]
|
|
96
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable validates outcome variable structure [0.67ms]
|
|
97
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable rejects additional properties [4.28ms]
|
|
98
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > ResponseVariable validates candidate responses [0.30ms]
|
|
99
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > ItemRef validates item references [0.39ms]
|
|
100
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > ItemPool validates item pool with multiple items [0.35ms]
|
|
101
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > SectionData validates complete section configuration [0.99ms]
|
|
102
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > ItemStage validates items to present to candidate [0.83ms]
|
|
103
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > AssessmentResult validates candidate responses and outcomes [1.19ms]
|
|
104
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > CatEngineResultReport validates CAT engine response [0.99ms]
|
|
105
|
+
(pass) CAT v1.0 Zod Schemas > Core data types > SessionInfo validates session state [0.90ms]
|
|
106
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionRequest validates section creation [0.29ms]
|
|
107
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionResponse validates section creation response [0.37ms]
|
|
108
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionRequest validates session creation [0.44ms]
|
|
109
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionResponse validates session creation response [0.32ms]
|
|
110
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsRequest validates result submission [0.40ms]
|
|
111
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsResponse validates CAT engine result report [0.08ms]
|
|
112
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSessionRequest validates session termination [0.29ms]
|
|
113
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSessionResponse validates session termination response [0.42ms]
|
|
114
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionRequest validates section closure [0.31ms]
|
|
115
|
+
(pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionResponse validates section closure response [0.19ms]
|
|
116
|
+
(pass) CAT v1.0 Zod Schemas > REST operation bindings > REST operation catalog exposes all 6 CAT operations [0.06ms]
|
|
117
|
+
(pass) CAT v1.0 Zod Schemas > REST operation bindings > CreateSection operation has correct structure [0.06ms]
|
|
118
|
+
(pass) CAT v1.0 Zod Schemas > REST operation bindings > SubmitResults operation has correct path template [0.03ms]
|
|
119
|
+
(pass) CAT v1.0 Zod Schemas > Derived templates > Cat10DerivedZodTemplates exposes specification metadata [0.59ms]
|
|
120
|
+
(pass) CAT v1.0 Zod Schemas > Derived templates > Specification links point to official CAT v1.0 resources [0.10ms]
|
|
121
|
+
(pass) CAT v1.0 Zod Schemas > Error handling > ErrorResponse validates error payloads [0.61ms]
|
|
117
122
|
|
|
118
123
|
test/schema-examples.test.ts:
|
|
119
|
-
(pass) example configs parse with RunnerConfigSchema [0.
|
|
120
|
-
(pass) summary sample shape validates [
|
|
121
|
-
(pass) requirement trace sample shape validates [
|
|
122
|
-
(pass) run metadata sample shape validates [1.
|
|
123
|
-
(pass) adapter profile sample shape validates [0.
|
|
124
|
+
(pass) example configs parse with RunnerConfigSchema [0.84ms]
|
|
125
|
+
(pass) summary sample shape validates [9.86ms]
|
|
126
|
+
(pass) requirement trace sample shape validates [1.02ms]
|
|
127
|
+
(pass) run metadata sample shape validates [1.19ms]
|
|
128
|
+
(pass) adapter profile sample shape validates [0.15ms]
|
|
124
129
|
|
|
125
130
|
test/clr-v2_0.test.ts:
|
|
126
|
-
(pass) ClrCredentialSchema parses a realistic CLR 2.0 credential [
|
|
127
|
-
(pass) AchievementCredentialSchema parses a minimal achievement credential [1.
|
|
128
|
-
(pass) ProfileSchema parses a minimal issuer profile [0.
|
|
129
|
-
(pass) GetClrCredentialsResponseSchema parses credential and JWS collections [0.
|
|
130
|
-
(pass) ImsxStatusInfoSchema enforces known status vocabularies [0.
|
|
131
|
-
(pass) ClrCredentialSchema rejects credentials without the CLR context set [0.
|
|
132
|
-
(pass) Clr20DerivedZodTemplates exposes the published entry points [0.
|
|
131
|
+
(pass) ClrCredentialSchema parses a realistic CLR 2.0 credential [7.26ms]
|
|
132
|
+
(pass) AchievementCredentialSchema parses a minimal achievement credential [1.91ms]
|
|
133
|
+
(pass) ProfileSchema parses a minimal issuer profile [0.08ms]
|
|
134
|
+
(pass) GetClrCredentialsResponseSchema parses credential and JWS collections [0.48ms]
|
|
135
|
+
(pass) ImsxStatusInfoSchema enforces known status vocabularies [0.61ms]
|
|
136
|
+
(pass) ClrCredentialSchema rejects credentials without the CLR context set [0.53ms]
|
|
137
|
+
(pass) Clr20DerivedZodTemplates exposes the published entry points [0.04ms]
|
|
133
138
|
|
|
134
139
|
test/cc-v1_4.test.ts:
|
|
135
|
-
(pass) CommonCartridgeV1_4 parses a minimal core manifest [3.
|
|
136
|
-
(pass) CommonCartridgeV1_4 rejects question-bank references from organization items [0.
|
|
137
|
-
(pass) CommonCartridgeV1_4 parses a minimal thin manifest with embedded webLink XML [2.
|
|
138
|
-
(pass) CommonCartridgeV1_4 thin profile rejects mismatched embedded resource XML [1.
|
|
139
|
-
(pass) CommonCartridgeV1_4 K-12 LOM resource profile enforces required educational metadata [2.
|
|
140
|
-
(pass) CommonCartridgeV1_4 parses assignment, line item, accessibility, and open video documents [
|
|
141
|
-
(pass) CommonCartridgeV1_4 exposes expected derived templates [0.
|
|
140
|
+
(pass) CommonCartridgeV1_4 parses a minimal core manifest [3.95ms]
|
|
141
|
+
(pass) CommonCartridgeV1_4 rejects question-bank references from organization items [0.97ms]
|
|
142
|
+
(pass) CommonCartridgeV1_4 parses a minimal thin manifest with embedded webLink XML [2.37ms]
|
|
143
|
+
(pass) CommonCartridgeV1_4 thin profile rejects mismatched embedded resource XML [1.45ms]
|
|
144
|
+
(pass) CommonCartridgeV1_4 K-12 LOM resource profile enforces required educational metadata [2.64ms]
|
|
145
|
+
(pass) CommonCartridgeV1_4 parses assignment, line item, accessibility, and open video documents [4.15ms]
|
|
146
|
+
(pass) CommonCartridgeV1_4 exposes expected derived templates [0.07ms]
|
|
142
147
|
|
|
143
148
|
test/open-badges-v3_0.test.ts:
|
|
144
|
-
(pass) AchievementCredentialSchema parses a realistic Open Badge credential [0.
|
|
145
|
-
(pass) EndorsementCredentialSchema parses a valid endorsement credential [1.
|
|
146
|
-
(pass) GetOpenBadgeCredentialsResponseSchema parses credential and JWS collections [0.
|
|
147
|
-
(pass) AchievementCredentialSchema rejects missing OB context [0.
|
|
149
|
+
(pass) AchievementCredentialSchema parses a realistic Open Badge credential [0.59ms]
|
|
150
|
+
(pass) EndorsementCredentialSchema parses a valid endorsement credential [1.68ms]
|
|
151
|
+
(pass) GetOpenBadgeCredentialsResponseSchema parses credential and JWS collections [0.33ms]
|
|
152
|
+
(pass) AchievementCredentialSchema rejects missing OB context [0.34ms]
|
|
148
153
|
(pass) OpenBadges30DerivedZodTemplates exposes expected entry points [0.04ms]
|
|
149
154
|
|
|
150
155
|
test/vc-data-model-v2_0.test.ts:
|
|
151
|
-
(pass) VerifiableCredentialSchema parses a minimal VC Data Model 2.0 credential [1.
|
|
152
|
-
(pass) VerifiablePresentationSchema parses a presentation carrying VC and JWS credentials [0.
|
|
153
|
-
(pass) VerifiableCredentialSchema rejects wrong core context [0.
|
|
154
|
-
(pass) VcDataModel20DerivedZodTemplates exposes expected entry points [0.
|
|
156
|
+
(pass) VerifiableCredentialSchema parses a minimal VC Data Model 2.0 credential [1.21ms]
|
|
157
|
+
(pass) VerifiablePresentationSchema parses a presentation carrying VC and JWS credentials [0.78ms]
|
|
158
|
+
(pass) VerifiableCredentialSchema rejects wrong core context [0.27ms]
|
|
159
|
+
(pass) VcDataModel20DerivedZodTemplates exposes expected entry points [0.05ms]
|
|
155
160
|
|
|
156
161
|
test/h5p-v1.test.ts:
|
|
157
|
-
(pass) H5P PackageManifest accepts a minimal valid h5p.json [
|
|
158
|
-
(pass) H5P PackageManifest accepts all optional fields [0.
|
|
159
|
-
(pass) H5P PackageManifest rejects missing required title [0.
|
|
162
|
+
(pass) H5P PackageManifest accepts a minimal valid h5p.json [4.37ms]
|
|
163
|
+
(pass) H5P PackageManifest accepts all optional fields [0.69ms]
|
|
164
|
+
(pass) H5P PackageManifest rejects missing required title [0.20ms]
|
|
160
165
|
(pass) H5P PackageManifest rejects invalid machine name [0.15ms]
|
|
161
|
-
(pass) H5P PackageManifest rejects invalid embed type [0.
|
|
162
|
-
(pass) H5P PackageManifest rejects yearTo < yearFrom [0.
|
|
163
|
-
(pass) H5P PackageManifest rejects duplicate embed types [0.
|
|
164
|
-
(pass) H5P LibraryManifest accepts a minimal valid library.json [1.
|
|
165
|
-
(pass) H5P LibraryManifest accepts a runnable library with full fields [
|
|
166
|
-
(pass) H5P LibraryManifest rejects invalid machineName [0.
|
|
167
|
-
(pass) H5P VersionRef rejects patch version (not part of the schema) [0.
|
|
168
|
-
(pass) H5P LibraryFolderName validates correct folder name [0.
|
|
169
|
-
(pass) H5P LibraryFolderName rejects folder names with patch version [0.
|
|
170
|
-
(pass) H5P Semantics accepts a simple text field [
|
|
171
|
-
(pass) H5P Semantics accepts a boolean field [
|
|
172
|
-
(pass) H5P Semantics accepts a select field with options [
|
|
173
|
-
(pass) H5P Semantics accepts a group with nested fields [
|
|
174
|
-
(pass) H5P Semantics accepts a list field containing a group [
|
|
175
|
-
(pass) H5P Semantics accepts deeply nested groups (3 levels) [0.
|
|
176
|
-
(pass) H5P Semantics accepts library field with options [0.
|
|
177
|
-
(pass) H5P Semantics accepts an image field [0.
|
|
178
|
-
(pass) H5P Semantics rejects unknown field type [1.
|
|
179
|
-
(pass) H5P Semantics rejects field without name [0.
|
|
180
|
-
(pass) H5P MediaFile accepts a valid image file reference [0.
|
|
181
|
-
(pass) H5P LibraryEmbed accepts a valid embedded library reference [0.
|
|
166
|
+
(pass) H5P PackageManifest rejects invalid embed type [0.22ms]
|
|
167
|
+
(pass) H5P PackageManifest rejects yearTo < yearFrom [0.16ms]
|
|
168
|
+
(pass) H5P PackageManifest rejects duplicate embed types [0.14ms]
|
|
169
|
+
(pass) H5P LibraryManifest accepts a minimal valid library.json [1.84ms]
|
|
170
|
+
(pass) H5P LibraryManifest accepts a runnable library with full fields [1.04ms]
|
|
171
|
+
(pass) H5P LibraryManifest rejects invalid machineName [0.22ms]
|
|
172
|
+
(pass) H5P VersionRef rejects patch version (not part of the schema) [0.08ms]
|
|
173
|
+
(pass) H5P LibraryFolderName validates correct folder name [0.07ms]
|
|
174
|
+
(pass) H5P LibraryFolderName rejects folder names with patch version [0.05ms]
|
|
175
|
+
(pass) H5P Semantics accepts a simple text field [1.57ms]
|
|
176
|
+
(pass) H5P Semantics accepts a boolean field [3.09ms]
|
|
177
|
+
(pass) H5P Semantics accepts a select field with options [4.20ms]
|
|
178
|
+
(pass) H5P Semantics accepts a group with nested fields [2.46ms]
|
|
179
|
+
(pass) H5P Semantics accepts a list field containing a group [2.35ms]
|
|
180
|
+
(pass) H5P Semantics accepts deeply nested groups (3 levels) [0.27ms]
|
|
181
|
+
(pass) H5P Semantics accepts library field with options [0.21ms]
|
|
182
|
+
(pass) H5P Semantics accepts an image field [0.12ms]
|
|
183
|
+
(pass) H5P Semantics rejects unknown field type [1.39ms]
|
|
184
|
+
(pass) H5P Semantics rejects field without name [0.50ms]
|
|
185
|
+
(pass) H5P MediaFile accepts a valid image file reference [0.77ms]
|
|
186
|
+
(pass) H5P LibraryEmbed accepts a valid embedded library reference [0.68ms]
|
|
182
187
|
|
|
183
188
|
test/lti.test.ts:
|
|
184
|
-
(pass) LTI core launch schema accepts a normalized resource link launch [
|
|
185
|
-
(pass) LTI deep linking schema accepts request settings and response items [
|
|
186
|
-
(pass) LTI AGS schema accepts endpoint, line item, score, and result shapes [1.
|
|
187
|
-
(pass) LTI NRPS schema accepts names/roles service and membership container shapes [
|
|
188
|
-
(pass) LTI proctoring schema accepts start and end assessment messages [
|
|
189
|
+
(pass) LTI core launch schema accepts a normalized resource link launch [2.65ms]
|
|
190
|
+
(pass) LTI deep linking schema accepts request settings and response items [2.43ms]
|
|
191
|
+
(pass) LTI AGS schema accepts endpoint, line item, score, and result shapes [1.90ms]
|
|
192
|
+
(pass) LTI NRPS schema accepts names/roles service and membership container shapes [2.02ms]
|
|
193
|
+
(pass) LTI proctoring schema accepts start and end assessment messages [2.01ms]
|
|
189
194
|
|
|
190
195
|
test/case-v1_1.test.ts:
|
|
191
|
-
(pass) CASE v1.1 Schemas > Core entity schemas > should validate a valid CFAssociation [
|
|
192
|
-
(pass) CASE v1.1 Schemas > Core entity schemas > should reject CFAssociation with invalid UUID [0.
|
|
196
|
+
(pass) CASE v1.1 Schemas > Core entity schemas > should validate a valid CFAssociation [1.22ms]
|
|
197
|
+
(pass) CASE v1.1 Schemas > Core entity schemas > should reject CFAssociation with invalid UUID [0.21ms]
|
|
193
198
|
(pass) CASE v1.1 Schemas > REST binding operations > should expose getCFAssociation operation [0.03ms]
|
|
194
199
|
(pass) CASE v1.1 Schemas > REST binding operations > should expose listCFAssociations operation [0.02ms]
|
|
195
|
-
(pass) CASE v1.1 Schemas > REST binding operations > should expose getCFPackage operation [0.
|
|
200
|
+
(pass) CASE v1.1 Schemas > REST binding operations > should expose getCFPackage operation [0.01ms]
|
|
196
201
|
(pass) CASE v1.1 Schemas > REST binding operations > should expose listCFItems operation [0.01ms]
|
|
197
202
|
(pass) CASE v1.1 Schemas > REST binding operations > should expose getCFRubric operation [0.01ms]
|
|
198
203
|
(pass) CASE v1.1 Schemas > Shared validators > should validate RFC4122 UUID [0.02ms]
|
|
199
|
-
(pass) CASE v1.1 Schemas > Shared validators > should reject invalid UUID format [0.
|
|
204
|
+
(pass) CASE v1.1 Schemas > Shared validators > should reject invalid UUID format [0.07ms]
|
|
200
205
|
(pass) CASE v1.1 Schemas > Shared validators > should validate ISO8601 datetime [0.02ms]
|
|
201
206
|
(pass) CASE v1.1 Schemas > Shared validators > should validate link URI [0.26ms]
|
|
202
|
-
(pass) CASE v1.1 Schemas > Extensible vocabularies > should allow standard association types [0.
|
|
203
|
-
(pass) CASE v1.1 Schemas > Extensible vocabularies > should allow ext:* custom extensions [0.
|
|
207
|
+
(pass) CASE v1.1 Schemas > Extensible vocabularies > should allow standard association types [0.29ms]
|
|
208
|
+
(pass) CASE v1.1 Schemas > Extensible vocabularies > should allow ext:* custom extensions [0.08ms]
|
|
204
209
|
(pass) CASE v1.1 Schemas > Extensible vocabularies > should reject invalid ext: format [0.09ms]
|
|
205
210
|
(pass) CASE v1.1 Schemas > Derived templates > should expose spec links [0.04ms]
|
|
206
211
|
(pass) CASE v1.1 Schemas > Derived templates > should document scope coverage [0.03ms]
|
|
207
212
|
|
|
208
213
|
test/caliper-v1_2.test.ts:
|
|
209
|
-
(pass) ActionSchema validates Caliper action vocabulary [0.
|
|
210
|
-
(pass) PersonSchema requires top-level @context for described entity documents [
|
|
211
|
-
(pass) EventSchema enforces URN UUID ids and UTC-millisecond eventTime [1.
|
|
212
|
-
(pass) AnnotationEventSchema enforces textual actor/action/object constraints [
|
|
213
|
-
(pass) MessageEventSchema accepts MarkedAsUnRead textual alias [0.
|
|
214
|
-
(pass) ViewEventSchema restricts target entity type to Frame [0.
|
|
215
|
-
(pass) EnvelopeSchema enforces required fields and disallows custom top-level properties [
|
|
216
|
-
(pass) TextPositionSelectorSchema requires type/start/end [0.
|
|
217
|
-
(pass) SystemIdentifierSchema validates required identifier fields [0.
|
|
218
|
-
(pass) Caliper12DerivedZodTemplates exposes key Caliper entry points and conformance metadata [0.
|
|
219
|
-
|
|
220
|
-
|
|
214
|
+
(pass) ActionSchema validates Caliper action vocabulary [0.28ms]
|
|
215
|
+
(pass) PersonSchema requires top-level @context for described entity documents [1.09ms]
|
|
216
|
+
(pass) EventSchema enforces URN UUID ids and UTC-millisecond eventTime [1.36ms]
|
|
217
|
+
(pass) AnnotationEventSchema enforces textual actor/action/object constraints [1.26ms]
|
|
218
|
+
(pass) MessageEventSchema accepts MarkedAsUnRead textual alias [0.64ms]
|
|
219
|
+
(pass) ViewEventSchema restricts target entity type to Frame [0.61ms]
|
|
220
|
+
(pass) EnvelopeSchema enforces required fields and disallows custom top-level properties [11.66ms]
|
|
221
|
+
(pass) TextPositionSelectorSchema requires type/start/end [0.85ms]
|
|
222
|
+
(pass) SystemIdentifierSchema validates required identifier fields [0.82ms]
|
|
223
|
+
(pass) Caliper12DerivedZodTemplates exposes key Caliper entry points and conformance metadata [0.11ms]
|
|
224
|
+
|
|
225
|
+
187 pass
|
|
221
226
|
0 fail
|
|
222
|
-
|
|
223
|
-
Ran
|
|
227
|
+
318 expect() calls
|
|
228
|
+
Ran 187 tests across 17 files. [841.00ms]
|
package/package.json
CHANGED
|
@@ -659,6 +659,8 @@ export const QtiPortableCustomInteractionSchema: z.ZodTypeAny = z.lazy(() =>
|
|
|
659
659
|
catalogInfo: QtiCatalogInfoSchema.optional(),
|
|
660
660
|
customInteractionTypeIdentifier: z.string(),
|
|
661
661
|
module: z.string().optional(),
|
|
662
|
+
/** PCI configuration properties: the element's data-* attributes, prefix stripped. */
|
|
663
|
+
properties: z.record(z.string(), z.string()).optional(),
|
|
662
664
|
}),
|
|
663
665
|
);
|
|
664
666
|
|
|
@@ -688,6 +690,8 @@ export const QtiEndAttemptInteractionSchema: z.ZodTypeAny = z.lazy(() =>
|
|
|
688
690
|
kind: z.literal("endAttemptInteraction"),
|
|
689
691
|
...QtiCommonNodeShape,
|
|
690
692
|
responseIdentifier: QtiIdentifierSchema.optional(),
|
|
693
|
+
// The XSD-required button label (`title` on qti-end-attempt-interaction).
|
|
694
|
+
title: z.string().optional(),
|
|
691
695
|
content: z.array(QtiContentFragmentSchema).optional(),
|
|
692
696
|
attributes: XmlForeignAttributesSchema.optional(),
|
|
693
697
|
}),
|
|
@@ -936,14 +940,16 @@ function validateOutcomeDeclarationConventions(
|
|
|
936
940
|
path: Array<string | number>,
|
|
937
941
|
) {
|
|
938
942
|
for (const [index, declaration] of declarations.entries()) {
|
|
943
|
+
// The information model recommends float, but the XSD does not enforce it and the
|
|
944
|
+
// official corpus ships integer SCOREs — require single numeric, nothing stricter.
|
|
939
945
|
if (
|
|
940
946
|
["SCORE", "MAXSCORE"].includes(declaration.identifier) &&
|
|
941
|
-
(declaration.baseType
|
|
947
|
+
(!["float", "integer"].includes(declaration.baseType ?? "") || declaration.cardinality !== "single")
|
|
942
948
|
) {
|
|
943
949
|
addIssue(
|
|
944
950
|
context,
|
|
945
951
|
[...path, index],
|
|
946
|
-
`${declaration.identifier} should
|
|
952
|
+
`${declaration.identifier} should be a single numeric outcome (baseType 'float' or 'integer').`,
|
|
947
953
|
);
|
|
948
954
|
}
|
|
949
955
|
|
|
@@ -1018,8 +1024,8 @@ function validateResponseBinding(
|
|
|
1018
1024
|
typeof choice.identifier === "string" ? [choice.identifier] : [],
|
|
1019
1025
|
),
|
|
1020
1026
|
);
|
|
1021
|
-
const minChoices =
|
|
1022
|
-
const maxChoices =
|
|
1027
|
+
const minChoices = minOf(interaction.minChoices);
|
|
1028
|
+
const maxChoices = boundedMax(interaction.maxChoices);
|
|
1023
1029
|
if (minChoices !== undefined && maxChoices !== undefined && minChoices > maxChoices) {
|
|
1024
1030
|
addIssue(context, path, "choiceInteraction minChoices must not exceed maxChoices.");
|
|
1025
1031
|
}
|
|
@@ -1052,8 +1058,8 @@ function validateResponseBinding(
|
|
|
1052
1058
|
break;
|
|
1053
1059
|
}
|
|
1054
1060
|
requireBaseAndCardinality(["string", "integer", "float"], ["single", "multiple", "ordered"]);
|
|
1055
|
-
const minStrings =
|
|
1056
|
-
const maxStrings =
|
|
1061
|
+
const minStrings = minOf(interaction.minStrings);
|
|
1062
|
+
const maxStrings = boundedMax(interaction.maxStrings);
|
|
1057
1063
|
if (minStrings !== undefined && maxStrings !== undefined && minStrings > maxStrings) {
|
|
1058
1064
|
addIssue(context, path, "extendedTextInteraction minStrings must not exceed maxStrings.");
|
|
1059
1065
|
}
|
|
@@ -1062,8 +1068,8 @@ function validateResponseBinding(
|
|
|
1062
1068
|
|
|
1063
1069
|
case "matchInteraction": {
|
|
1064
1070
|
requireBaseAndCardinality(["directedPair"], ["single", "multiple"]);
|
|
1065
|
-
const minAssociations =
|
|
1066
|
-
const maxAssociations =
|
|
1071
|
+
const minAssociations = minOf(interaction.minAssociations);
|
|
1072
|
+
const maxAssociations = boundedMax(interaction.maxAssociations);
|
|
1067
1073
|
if (minAssociations !== undefined && maxAssociations !== undefined && minAssociations > maxAssociations) {
|
|
1068
1074
|
addIssue(context, path, "matchInteraction minAssociations must not exceed maxAssociations.");
|
|
1069
1075
|
}
|
|
@@ -1080,8 +1086,8 @@ function validateResponseBinding(
|
|
|
1080
1086
|
typeof choice.identifier === "string" ? [choice.identifier] : [],
|
|
1081
1087
|
),
|
|
1082
1088
|
);
|
|
1083
|
-
const minAssociations =
|
|
1084
|
-
const maxAssociations =
|
|
1089
|
+
const minAssociations = minOf(interaction.minAssociations);
|
|
1090
|
+
const maxAssociations = boundedMax(interaction.maxAssociations);
|
|
1085
1091
|
if (minAssociations !== undefined && maxAssociations !== undefined && minAssociations > maxAssociations) {
|
|
1086
1092
|
addIssue(context, path, "gapMatchInteraction minAssociations must not exceed maxAssociations.");
|
|
1087
1093
|
}
|
|
@@ -1090,8 +1096,8 @@ function validateResponseBinding(
|
|
|
1090
1096
|
|
|
1091
1097
|
case "mediaInteraction": {
|
|
1092
1098
|
requireBaseAndCardinality(["integer"], ["single"]);
|
|
1093
|
-
const minPlays =
|
|
1094
|
-
const maxPlays =
|
|
1099
|
+
const minPlays = minOf(interaction.minPlays);
|
|
1100
|
+
const maxPlays = boundedMax(interaction.maxPlays);
|
|
1095
1101
|
if (minPlays !== undefined && maxPlays !== undefined && minPlays > maxPlays) {
|
|
1096
1102
|
addIssue(context, path, "mediaInteraction minPlays must not exceed maxPlays.");
|
|
1097
1103
|
}
|
|
@@ -1139,8 +1145,8 @@ function validateResponseBinding(
|
|
|
1139
1145
|
typeof choice.identifier === "string" ? [choice.identifier] : [],
|
|
1140
1146
|
),
|
|
1141
1147
|
);
|
|
1142
|
-
const minChoices =
|
|
1143
|
-
const maxChoices =
|
|
1148
|
+
const minChoices = minOf(interaction.minChoices);
|
|
1149
|
+
const maxChoices = boundedMax(interaction.maxChoices);
|
|
1144
1150
|
if (minChoices !== undefined && maxChoices !== undefined && minChoices > maxChoices) {
|
|
1145
1151
|
addIssue(context, path, "hotspotInteraction minChoices must not exceed maxChoices.");
|
|
1146
1152
|
}
|
|
@@ -1150,8 +1156,8 @@ function validateResponseBinding(
|
|
|
1150
1156
|
case "associateInteraction":
|
|
1151
1157
|
case "graphicAssociateInteraction": {
|
|
1152
1158
|
requireBaseAndCardinality(["pair"], ["single", "multiple"]);
|
|
1153
|
-
const minAssociations =
|
|
1154
|
-
const maxAssociations =
|
|
1159
|
+
const minAssociations = minOf(interaction.minAssociations);
|
|
1160
|
+
const maxAssociations = boundedMax(interaction.maxAssociations);
|
|
1155
1161
|
if (minAssociations !== undefined && maxAssociations !== undefined && minAssociations > maxAssociations) {
|
|
1156
1162
|
addIssue(context, path, `${kind} minAssociations must not exceed maxAssociations.`);
|
|
1157
1163
|
}
|
|
@@ -1171,8 +1177,8 @@ function validateResponseBinding(
|
|
|
1171
1177
|
case "selectPointInteraction":
|
|
1172
1178
|
case "positionObjectInteraction": {
|
|
1173
1179
|
requireBaseAndCardinality(["point"], ["single", "multiple"]);
|
|
1174
|
-
const minChoices =
|
|
1175
|
-
const maxChoices =
|
|
1180
|
+
const minChoices = minOf(interaction.minChoices);
|
|
1181
|
+
const maxChoices = boundedMax(interaction.maxChoices);
|
|
1176
1182
|
if (minChoices !== undefined && maxChoices !== undefined && minChoices > maxChoices) {
|
|
1177
1183
|
addIssue(context, path, `${kind} minChoices must not exceed maxChoices.`);
|
|
1178
1184
|
}
|
|
@@ -1191,6 +1197,13 @@ function validateResponseBinding(
|
|
|
1191
1197
|
}
|
|
1192
1198
|
}
|
|
1193
1199
|
|
|
1200
|
+
/**
|
|
1201
|
+
* Built-in session outcomes every QTI item declares implicitly. `completion_status`
|
|
1202
|
+
* is the snake_case authoring of the same built-in that the official corpus ships
|
|
1203
|
+
* (the runtime treats it as an alias when reading adaptive completion).
|
|
1204
|
+
*/
|
|
1205
|
+
const builtInOutcomeIdentifiers = new Set(["completionStatus", "completion_status"]);
|
|
1206
|
+
|
|
1194
1207
|
function validateOutcomeReferences(
|
|
1195
1208
|
value: unknown,
|
|
1196
1209
|
declaredOutcomes: Map<string, unknown>,
|
|
@@ -1199,12 +1212,21 @@ function validateOutcomeReferences(
|
|
|
1199
1212
|
) {
|
|
1200
1213
|
for (const rule of collectNodesByKind(value, ["setOutcomeValue", "lookupOutcomeValue"])) {
|
|
1201
1214
|
const identifier = typeof rule.identifier === "string" ? rule.identifier : undefined;
|
|
1202
|
-
if (identifier && !declaredOutcomes.has(identifier)) {
|
|
1215
|
+
if (identifier && !declaredOutcomes.has(identifier) && !builtInOutcomeIdentifiers.has(identifier)) {
|
|
1203
1216
|
addIssue(context, path, `Processing rule references undeclared outcome identifier '${identifier}'.`);
|
|
1204
1217
|
}
|
|
1205
1218
|
}
|
|
1206
1219
|
}
|
|
1207
1220
|
|
|
1221
|
+
/** QTI max-* attributes use 0 to mean "unbounded"; bound checks must ignore it. */
|
|
1222
|
+
function boundedMax(value: unknown): number | undefined {
|
|
1223
|
+
return typeof value === "number" && value > 0 ? value : undefined;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
function minOf(value: unknown): number | undefined {
|
|
1227
|
+
return typeof value === "number" ? value : undefined;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1208
1230
|
function validateTemplateAndResponseReferences(
|
|
1209
1231
|
value: unknown,
|
|
1210
1232
|
declaredTemplates: Map<string, unknown>,
|
|
@@ -33,7 +33,6 @@ const binaryExpressionKinds = [
|
|
|
33
33
|
"divide",
|
|
34
34
|
"delete",
|
|
35
35
|
"match",
|
|
36
|
-
"equal",
|
|
37
36
|
"integerDivide",
|
|
38
37
|
"integerModulus",
|
|
39
38
|
"contains",
|
|
@@ -237,6 +236,17 @@ export const QtiAnyNExpressionSchema: z.ZodTypeAny = z.lazy(() =>
|
|
|
237
236
|
}),
|
|
238
237
|
);
|
|
239
238
|
|
|
239
|
+
export const QtiEqualExpressionSchema: z.ZodTypeAny = z.lazy(() =>
|
|
240
|
+
strictObject({
|
|
241
|
+
kind: z.literal("equal"),
|
|
242
|
+
children: z.tuple([QtiExpressionSchema, QtiExpressionSchema]),
|
|
243
|
+
toleranceMode: z.enum(["exact", "absolute", "relative"]).optional(),
|
|
244
|
+
tolerance: z.array(QtiNumberOrVariableSchema).min(1).max(2).optional(),
|
|
245
|
+
includeLowerBound: z.boolean().optional(),
|
|
246
|
+
includeUpperBound: z.boolean().optional(),
|
|
247
|
+
}),
|
|
248
|
+
);
|
|
249
|
+
|
|
240
250
|
export const QtiEqualRoundedExpressionSchema: z.ZodTypeAny = z.lazy(() =>
|
|
241
251
|
strictObject({
|
|
242
252
|
kind: z.literal("equalRounded"),
|
|
@@ -412,6 +422,7 @@ export const QtiExpressionSchema: z.ZodTypeAny = z.lazy(() =>
|
|
|
412
422
|
QtiContainerExpressionSchema,
|
|
413
423
|
QtiCustomOperatorExpressionSchema,
|
|
414
424
|
QtiAnyNExpressionSchema,
|
|
425
|
+
QtiEqualExpressionSchema,
|
|
415
426
|
QtiEqualRoundedExpressionSchema,
|
|
416
427
|
QtiFieldValueExpressionSchema,
|
|
417
428
|
QtiIndexExpressionSchema,
|
package/test/qti-v3_0_1.test.ts
CHANGED
|
@@ -574,3 +574,111 @@ test("QTI barrel exports prefixed XML extension node helpers", () => {
|
|
|
574
574
|
|
|
575
575
|
expect(parsed.success).toBe(true);
|
|
576
576
|
});
|
|
577
|
+
|
|
578
|
+
test("built-in outcome completionStatus needs no declaration in processing rules", () => {
|
|
579
|
+
const parsed = QtiAssessmentItemDocumentSchema.safeParse({
|
|
580
|
+
assessmentItem: {
|
|
581
|
+
identifier: "adaptive-1",
|
|
582
|
+
title: "Adaptive item",
|
|
583
|
+
timeDependent: false,
|
|
584
|
+
adaptive: true,
|
|
585
|
+
responseDeclarations: [{ identifier: "RESPONSE", cardinality: "single", baseType: "identifier" }],
|
|
586
|
+
outcomeDeclarations: [{ identifier: "SCORE", cardinality: "single", baseType: "float" }],
|
|
587
|
+
itemBody: { content: [{ kind: "xml", name: "p", value: "Stem" }] },
|
|
588
|
+
responseProcessing: {
|
|
589
|
+
rules: [
|
|
590
|
+
{
|
|
591
|
+
kind: "setOutcomeValue",
|
|
592
|
+
identifier: "completionStatus",
|
|
593
|
+
expression: { kind: "baseValue", baseType: "identifier", value: "completed" },
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
expect(parsed.success).toBe(true);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("an integer SCORE is accepted — the official corpus ships it", () => {
|
|
604
|
+
const parsed = QtiAssessmentItemDocumentSchema.safeParse({
|
|
605
|
+
assessmentItem: {
|
|
606
|
+
identifier: "integer-score-1",
|
|
607
|
+
title: "Integer score",
|
|
608
|
+
timeDependent: false,
|
|
609
|
+
responseDeclarations: [{ identifier: "RESPONSE", cardinality: "single", baseType: "identifier" }],
|
|
610
|
+
outcomeDeclarations: [{ identifier: "SCORE", cardinality: "single", baseType: "integer" }],
|
|
611
|
+
itemBody: { content: [{ kind: "xml", name: "p", value: "Stem" }] },
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
expect(parsed.success).toBe(true);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
test("a non-numeric or multiple SCORE is still rejected", () => {
|
|
619
|
+
const parsed = QtiAssessmentItemDocumentSchema.safeParse({
|
|
620
|
+
assessmentItem: {
|
|
621
|
+
identifier: "string-score-1",
|
|
622
|
+
title: "String score",
|
|
623
|
+
timeDependent: false,
|
|
624
|
+
responseDeclarations: [{ identifier: "RESPONSE", cardinality: "single", baseType: "identifier" }],
|
|
625
|
+
outcomeDeclarations: [{ identifier: "SCORE", cardinality: "single", baseType: "string" }],
|
|
626
|
+
itemBody: { content: [{ kind: "xml", name: "p", value: "Stem" }] },
|
|
627
|
+
},
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
expect(parsed.success).toBe(false);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
test("snake_case completion_status is accepted as the built-in's corpus alias", () => {
|
|
634
|
+
const parsed = QtiAssessmentItemDocumentSchema.safeParse({
|
|
635
|
+
assessmentItem: {
|
|
636
|
+
identifier: "adaptive-2",
|
|
637
|
+
title: "Adaptive item (snake_case authoring)",
|
|
638
|
+
timeDependent: false,
|
|
639
|
+
adaptive: true,
|
|
640
|
+
responseDeclarations: [{ identifier: "RESPONSE", cardinality: "single", baseType: "identifier" }],
|
|
641
|
+
outcomeDeclarations: [{ identifier: "SCORE", cardinality: "single", baseType: "float" }],
|
|
642
|
+
itemBody: { content: [{ kind: "xml", name: "p", value: "Stem" }] },
|
|
643
|
+
responseProcessing: {
|
|
644
|
+
rules: [
|
|
645
|
+
{
|
|
646
|
+
kind: "setOutcomeValue",
|
|
647
|
+
identifier: "completion_status",
|
|
648
|
+
expression: { kind: "baseValue", baseType: "identifier", value: "completed" },
|
|
649
|
+
},
|
|
650
|
+
],
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
expect(parsed.success).toBe(true);
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
test("maxChoices 0 means unbounded and never conflicts with minChoices", () => {
|
|
659
|
+
const parsed = QtiAssessmentItemDocumentSchema.safeParse({
|
|
660
|
+
assessmentItem: {
|
|
661
|
+
identifier: "multi-1",
|
|
662
|
+
title: "Pick at least two",
|
|
663
|
+
timeDependent: false,
|
|
664
|
+
responseDeclarations: [{ identifier: "RESPONSE", cardinality: "multiple", baseType: "identifier" }],
|
|
665
|
+
itemBody: {
|
|
666
|
+
content: [
|
|
667
|
+
{
|
|
668
|
+
kind: "choiceInteraction",
|
|
669
|
+
responseIdentifier: "RESPONSE",
|
|
670
|
+
maxChoices: 0,
|
|
671
|
+
minChoices: 2,
|
|
672
|
+
simpleChoices: [
|
|
673
|
+
{ kind: "simpleChoice", identifier: "A" },
|
|
674
|
+
{ kind: "simpleChoice", identifier: "B" },
|
|
675
|
+
{ kind: "simpleChoice", identifier: "C" },
|
|
676
|
+
],
|
|
677
|
+
},
|
|
678
|
+
],
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
expect(parsed.success).toBe(true);
|
|
684
|
+
});
|