@longsightgroup/qti3-core 0.9.2 → 0.9.3
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/README.md +20 -3
- package/dist/assert-never.d.ts +3 -0
- package/dist/assert-never.d.ts.map +1 -0
- package/dist/assert-never.js +5 -0
- package/dist/assert-never.js.map +1 -0
- package/dist/attempt-state-constants.d.ts +6 -0
- package/dist/attempt-state-constants.d.ts.map +1 -0
- package/dist/attempt-state-constants.js +6 -0
- package/dist/attempt-state-constants.js.map +1 -0
- package/dist/attempt-state.d.ts +5 -0
- package/dist/attempt-state.d.ts.map +1 -0
- package/dist/attempt-state.js +190 -0
- package/dist/attempt-state.js.map +1 -0
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/content-text.d.ts.map +1 -1
- package/dist/content-text.js +3 -0
- package/dist/content-text.js.map +1 -1
- package/dist/custom-operators.d.ts +13 -0
- package/dist/custom-operators.d.ts.map +1 -0
- package/dist/custom-operators.js +2 -0
- package/dist/custom-operators.js.map +1 -0
- package/dist/delivery-redaction.js +1 -1
- package/dist/delivery-redaction.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parser-custom-interactions.js +1 -1
- package/dist/parser-custom-interactions.js.map +1 -1
- package/dist/parser-declarations.d.ts.map +1 -1
- package/dist/parser-declarations.js +4 -4
- package/dist/parser-declarations.js.map +1 -1
- package/dist/parser-item-metadata.d.ts.map +1 -1
- package/dist/parser-item-metadata.js +8 -7
- package/dist/parser-item-metadata.js.map +1 -1
- package/dist/parser-processing.js +2 -2
- package/dist/parser-processing.js.map +1 -1
- package/dist/parser-values.d.ts +5 -1
- package/dist/parser-values.d.ts.map +1 -1
- package/dist/parser-values.js +33 -0
- package/dist/parser-values.js.map +1 -1
- package/dist/processing-evaluator-boolean.d.ts +8 -0
- package/dist/processing-evaluator-boolean.d.ts.map +1 -0
- package/dist/processing-evaluator-boolean.js +47 -0
- package/dist/processing-evaluator-boolean.js.map +1 -0
- package/dist/processing-evaluator-collection.d.ts +12 -0
- package/dist/processing-evaluator-collection.d.ts.map +1 -0
- package/dist/processing-evaluator-collection.js +35 -0
- package/dist/processing-evaluator-collection.js.map +1 -0
- package/dist/processing-evaluator-comparison.d.ts +8 -0
- package/dist/processing-evaluator-comparison.d.ts.map +1 -0
- package/dist/processing-evaluator-comparison.js +88 -0
- package/dist/processing-evaluator-comparison.js.map +1 -0
- package/dist/processing-evaluator-custom.d.ts +8 -0
- package/dist/processing-evaluator-custom.d.ts.map +1 -0
- package/dist/processing-evaluator-custom.js +14 -0
- package/dist/processing-evaluator-custom.js.map +1 -0
- package/dist/processing-evaluator-geometry.d.ts +8 -0
- package/dist/processing-evaluator-geometry.d.ts.map +1 -0
- package/dist/processing-evaluator-geometry.js +21 -0
- package/dist/processing-evaluator-geometry.js.map +1 -0
- package/dist/processing-evaluator-numeric.d.ts +8 -0
- package/dist/processing-evaluator-numeric.d.ts.map +1 -0
- package/dist/processing-evaluator-numeric.js +112 -0
- package/dist/processing-evaluator-numeric.js.map +1 -0
- package/dist/processing-evaluator-string.d.ts +8 -0
- package/dist/processing-evaluator-string.d.ts.map +1 -0
- package/dist/processing-evaluator-string.js +33 -0
- package/dist/processing-evaluator-string.js.map +1 -0
- package/dist/processing-evaluator-variable.d.ts +8 -0
- package/dist/processing-evaluator-variable.d.ts.map +1 -0
- package/dist/processing-evaluator-variable.js +39 -0
- package/dist/processing-evaluator-variable.js.map +1 -0
- package/dist/processing-evaluator.d.ts +22 -0
- package/dist/processing-evaluator.d.ts.map +1 -0
- package/dist/processing-evaluator.js +137 -0
- package/dist/processing-evaluator.js.map +1 -0
- package/dist/processing-expression-children.d.ts +3 -0
- package/dist/processing-expression-children.d.ts.map +1 -0
- package/dist/processing-expression-children.js +68 -0
- package/dist/processing-expression-children.js.map +1 -0
- package/dist/processing-mapping.d.ts +13 -0
- package/dist/processing-mapping.d.ts.map +1 -0
- package/dist/processing-mapping.js +120 -0
- package/dist/processing-mapping.js.map +1 -0
- package/dist/processing-operators.d.ts +11 -0
- package/dist/processing-operators.d.ts.map +1 -0
- package/dist/processing-operators.js +178 -0
- package/dist/processing-operators.js.map +1 -0
- package/dist/processing-random.d.ts +2 -0
- package/dist/processing-random.d.ts.map +1 -0
- package/dist/processing-random.js +16 -0
- package/dist/processing-random.js.map +1 -0
- package/dist/processing-state.d.ts +8 -0
- package/dist/processing-state.d.ts.map +1 -0
- package/dist/processing-state.js +44 -0
- package/dist/processing-state.js.map +1 -0
- package/dist/processing-templates.d.ts +3 -0
- package/dist/processing-templates.d.ts.map +1 -0
- package/dist/processing-templates.js +12 -0
- package/dist/processing-templates.js.map +1 -0
- package/dist/processing-values.d.ts +15 -0
- package/dist/processing-values.d.ts.map +1 -0
- package/dist/processing-values.js +150 -0
- package/dist/processing-values.js.map +1 -0
- package/dist/processing-variables.d.ts +7 -0
- package/dist/processing-variables.d.ts.map +1 -0
- package/dist/processing-variables.js +40 -0
- package/dist/processing-variables.js.map +1 -0
- package/dist/serializer-processing-expressions.d.ts.map +1 -1
- package/dist/serializer-processing-expressions.js +2 -8
- package/dist/serializer-processing-expressions.js.map +1 -1
- package/dist/serializer-processing-xml.js +2 -2
- package/dist/serializer-processing-xml.js.map +1 -1
- package/dist/serializer-processing.d.ts.map +1 -1
- package/dist/serializer-processing.js +3 -0
- package/dist/serializer-processing.js.map +1 -1
- package/dist/session.d.ts +5 -14
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +76 -1158
- package/dist/session.js.map +1 -1
- package/dist/shared-vocabulary-coverage-policy.js +1 -1
- package/dist/shared-vocabulary-coverage-policy.js.map +1 -1
- package/dist/shared-vocabulary-generated-families.js +1 -1
- package/dist/shared-vocabulary-generated-families.js.map +1 -1
- package/dist/shared-vocabulary-support.js +3 -3
- package/dist/shared-vocabulary-support.js.map +1 -1
- package/dist/shared-vocabulary.d.ts.map +1 -1
- package/dist/shared-vocabulary.js +4 -3
- package/dist/shared-vocabulary.js.map +1 -1
- package/dist/support-evidence.d.ts +22 -0
- package/dist/support-evidence.d.ts.map +1 -0
- package/dist/support-evidence.js +93 -0
- package/dist/support-evidence.js.map +1 -0
- package/dist/support.d.ts.map +1 -1
- package/dist/support.js +30 -43
- package/dist/support.js.map +1 -1
- package/dist/trusted-item-session.d.ts.map +1 -1
- package/dist/trusted-item-session.js +6 -5
- package/dist/trusted-item-session.js.map +1 -1
- package/dist/tts.d.ts.map +1 -1
- package/dist/tts.js +16 -2
- package/dist/tts.js.map +1 -1
- package/dist/validation-geometry.d.ts +8 -0
- package/dist/validation-geometry.d.ts.map +1 -0
- package/dist/validation-geometry.js +187 -0
- package/dist/validation-geometry.js.map +1 -0
- package/dist/validation-interactions.d.ts +3 -0
- package/dist/validation-interactions.d.ts.map +1 -0
- package/dist/validation-interactions.js +644 -0
- package/dist/validation-interactions.js.map +1 -0
- package/dist/validation-primitives.d.ts +11 -0
- package/dist/validation-primitives.d.ts.map +1 -0
- package/dist/validation-primitives.js +48 -0
- package/dist/validation-primitives.js.map +1 -0
- package/dist/validation-processing.d.ts +4 -0
- package/dist/validation-processing.d.ts.map +1 -0
- package/dist/validation-processing.js +569 -0
- package/dist/validation-processing.js.map +1 -0
- package/dist/validation-shared-vocabulary-layout.d.ts +10 -0
- package/dist/validation-shared-vocabulary-layout.d.ts.map +1 -0
- package/dist/validation-shared-vocabulary-layout.js +102 -0
- package/dist/validation-shared-vocabulary-layout.js.map +1 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +27 -1641
- package/dist/validation.js.map +1 -1
- package/dist/xml.js +1 -1
- package/dist/xml.js.map +1 -1
- package/package.json +1 -1
- package/src/assert-never.ts +4 -0
- package/src/attempt-state-constants.ts +5 -0
- package/src/attempt-state.ts +245 -0
- package/src/catalog.ts +1 -1
- package/src/content-text.ts +3 -0
- package/src/custom-operators.ts +12 -0
- package/src/delivery-redaction.ts +1 -1
- package/src/index.ts +2 -0
- package/src/parser-custom-interactions.ts +1 -1
- package/src/parser-declarations.ts +4 -3
- package/src/parser-item-metadata.ts +8 -7
- package/src/parser-processing.ts +2 -2
- package/src/parser-values.ts +36 -0
- package/src/processing-evaluator-boolean.ts +48 -0
- package/src/processing-evaluator-collection.ts +50 -0
- package/src/processing-evaluator-comparison.ts +106 -0
- package/src/processing-evaluator-custom.ts +20 -0
- package/src/processing-evaluator-geometry.ts +26 -0
- package/src/processing-evaluator-numeric.ts +155 -0
- package/src/processing-evaluator-string.ts +60 -0
- package/src/processing-evaluator-variable.ts +81 -0
- package/src/processing-evaluator.ts +173 -0
- package/src/processing-expression-children.ts +69 -0
- package/src/processing-mapping.ts +153 -0
- package/src/processing-operators.ts +184 -0
- package/src/processing-random.ts +16 -0
- package/src/processing-state.ts +69 -0
- package/src/processing-templates.ts +12 -0
- package/src/processing-values.ts +162 -0
- package/src/processing-variables.ts +83 -0
- package/src/serializer-processing-expressions.ts +2 -12
- package/src/serializer-processing-xml.ts +2 -2
- package/src/serializer-processing.ts +3 -0
- package/src/session.ts +128 -2006
- package/src/shared-vocabulary-coverage-policy.ts +1 -1
- package/src/shared-vocabulary-generated-families.ts +1 -1
- package/src/shared-vocabulary-support.ts +3 -3
- package/src/shared-vocabulary.ts +3 -2
- package/src/support-evidence.ts +107 -0
- package/src/support.ts +59 -50
- package/src/trusted-item-session.ts +6 -4
- package/src/tts.ts +29 -15
- package/src/validation-geometry.ts +204 -0
- package/src/validation-interactions.ts +831 -0
- package/src/validation-primitives.ts +63 -0
- package/src/validation-processing.ts +785 -0
- package/src/validation-shared-vocabulary-layout.ts +117 -0
- package/src/validation.ts +44 -2077
- package/src/xml.ts +1 -1
|
@@ -48,7 +48,7 @@ export function findSharedVocabularyCoverageViolations(input: {
|
|
|
48
48
|
supportByClass.set(entry.className, existing);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const gatedSupportClasses = [...supportByClass.keys()].
|
|
51
|
+
const gatedSupportClasses = [...supportByClass.keys()].toSorted();
|
|
52
52
|
if (gatedSupportClasses.length === 0) {
|
|
53
53
|
violations.push({ message: "expected at least one enforced shared vocabulary class" });
|
|
54
54
|
}
|
|
@@ -122,7 +122,7 @@ function escapeRegExp(value: string): string {
|
|
|
122
122
|
|
|
123
123
|
function alternationPattern(values: readonly string[]): string {
|
|
124
124
|
return [...values]
|
|
125
|
-
.
|
|
125
|
+
.toSorted((left, right) => right.length - left.length)
|
|
126
126
|
.map(escapeRegExp)
|
|
127
127
|
.join("|");
|
|
128
128
|
}
|
|
@@ -52,7 +52,7 @@ const graphicBrowserTests = [
|
|
|
52
52
|
"tests/browser/player-graphic.spec.ts",
|
|
53
53
|
"tests/browser/player-graphic-gap-match.spec.ts",
|
|
54
54
|
];
|
|
55
|
-
const mediaBrowserTests = ["tests/browser/player.spec.ts", ...sharedVocabularyMatrixTests];
|
|
55
|
+
const mediaBrowserTests = ["tests/browser/player-media.spec.ts", ...sharedVocabularyMatrixTests];
|
|
56
56
|
const mediaPlayerFixture =
|
|
57
57
|
"packages/fixtures/packages/sv-matrix/items/media-controls-and-pause.xml";
|
|
58
58
|
const interactionInputWidthFixtures = [
|
|
@@ -204,14 +204,14 @@ function registryAttributeSupportProfile(fieldId: string): {
|
|
|
204
204
|
fixtures: [mediaPlayerFixture],
|
|
205
205
|
tests: mediaBrowserTests,
|
|
206
206
|
notes:
|
|
207
|
-
"Reflects authored pause-delay values on rendered media assets. Pause timer behavior is covered in tests/browser/player.spec.ts.",
|
|
207
|
+
"Reflects authored pause-delay values on rendered media assets. Pause timer behavior is covered in tests/browser/player-media.spec.ts.",
|
|
208
208
|
};
|
|
209
209
|
case "media-player-pause-duration":
|
|
210
210
|
return {
|
|
211
211
|
fixtures: [mediaPlayerFixture],
|
|
212
212
|
tests: mediaBrowserTests,
|
|
213
213
|
notes:
|
|
214
|
-
"Reflects authored pause-duration values on rendered media assets. Pause timer behavior is covered in tests/browser/player.spec.ts.",
|
|
214
|
+
"Reflects authored pause-duration values on rendered media assets. Pause timer behavior is covered in tests/browser/player-media.spec.ts.",
|
|
215
215
|
};
|
|
216
216
|
default:
|
|
217
217
|
return {
|
package/src/shared-vocabulary.ts
CHANGED
|
@@ -73,9 +73,10 @@ export function firstMatchingSharedVocabularyClass<T extends string>(
|
|
|
73
73
|
classNames: string[],
|
|
74
74
|
tokens: readonly T[],
|
|
75
75
|
): T | undefined {
|
|
76
|
-
const tokenSet = new Set<string>(tokens);
|
|
77
76
|
for (const className of classNames) {
|
|
78
|
-
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
if (token === className) return token;
|
|
79
|
+
}
|
|
79
80
|
}
|
|
80
81
|
return undefined;
|
|
81
82
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { QtiInteractionType } from "./types.js";
|
|
2
|
+
|
|
3
|
+
const coreProcessingTest = "packages/core/src/core.test.ts";
|
|
4
|
+
|
|
5
|
+
export const browserProcessingTests = ["tests/browser/player-processing.spec.ts"];
|
|
6
|
+
export const browserFeedbackTests = ["tests/browser/player-feedback.spec.ts"];
|
|
7
|
+
export const browserAdaptiveTests = ["tests/browser/player-adaptive.spec.ts"];
|
|
8
|
+
export const browserBodyContentTests = ["tests/browser/player-body-content.spec.ts"];
|
|
9
|
+
export const browserHarnessTests = ["tests/browser/player-harness.spec.ts"];
|
|
10
|
+
export const browserMathmlTests = ["tests/browser/player-mathml.spec.ts"];
|
|
11
|
+
export const browserKeyboardA11yTests = ["tests/browser/player-keyboard-a11y.spec.ts"];
|
|
12
|
+
export const browserLifecycleTests = ["tests/browser/player-lifecycle.spec.ts"];
|
|
13
|
+
|
|
14
|
+
const templateProcessingBrowserEvidence = [
|
|
15
|
+
...browserProcessingTests,
|
|
16
|
+
...browserBodyContentTests,
|
|
17
|
+
...browserMathmlTests,
|
|
18
|
+
];
|
|
19
|
+
const responseProcessingBrowserEvidence = [
|
|
20
|
+
...browserProcessingTests,
|
|
21
|
+
...browserFeedbackTests,
|
|
22
|
+
...browserAdaptiveTests,
|
|
23
|
+
];
|
|
24
|
+
const randomIntegerBrowserEvidence = browserProcessingTests;
|
|
25
|
+
|
|
26
|
+
/** Browser evidence bundles keyed by parent processing constructs only. */
|
|
27
|
+
export const processingBrowserEvidence = {
|
|
28
|
+
"qti-template-processing": templateProcessingBrowserEvidence,
|
|
29
|
+
"qti-response-processing": responseProcessingBrowserEvidence,
|
|
30
|
+
"qti-random-integer": randomIntegerBrowserEvidence,
|
|
31
|
+
} as const satisfies Record<string, readonly string[]>;
|
|
32
|
+
|
|
33
|
+
export type ProcessingBrowserEvidenceName = keyof typeof processingBrowserEvidence;
|
|
34
|
+
|
|
35
|
+
export const interactionExtraFixtures: Partial<Record<QtiInteractionType, readonly string[]>> = {
|
|
36
|
+
extendedText: [
|
|
37
|
+
"packages/fixtures/packages/sv-matrix/items/extended-text-pattern-mask.xml",
|
|
38
|
+
"packages/fixtures/packages/sv-matrix/items/extended-text-xhtml.xml",
|
|
39
|
+
],
|
|
40
|
+
textEntry: ["packages/fixtures/packages/sv-matrix/items/text-entry-pattern-mask-inline.xml"],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export function browserTestsFor(interactionType: QtiInteractionType): string[] {
|
|
44
|
+
const base = [
|
|
45
|
+
"packages/fixtures/src/fixtures.test.ts",
|
|
46
|
+
"packages/conformance/src/conformance.test.ts",
|
|
47
|
+
"packages/a11y/src/a11y.test.ts",
|
|
48
|
+
"tests/browser/player-interaction-sweep.spec.ts",
|
|
49
|
+
];
|
|
50
|
+
const extras: Partial<Record<QtiInteractionType, string[]>> = {
|
|
51
|
+
associate: browserKeyboardA11yTests,
|
|
52
|
+
choice: ["tests/browser/player-choice.spec.ts", "tests/browser/player-dom-behavior.spec.ts"],
|
|
53
|
+
drawing: ["tests/browser/player-graphic.spec.ts"],
|
|
54
|
+
endAttempt: ["tests/browser/player-dom-behavior.spec.ts", ...browserKeyboardA11yTests],
|
|
55
|
+
extendedText: [
|
|
56
|
+
"tests/browser/player-dom-behavior.spec.ts",
|
|
57
|
+
"tests/browser/player-extended-text-xhtml.spec.ts",
|
|
58
|
+
],
|
|
59
|
+
gapMatch: ["tests/browser/player-gap-match.spec.ts"],
|
|
60
|
+
graphicAssociate: ["tests/browser/player-graphic.spec.ts"],
|
|
61
|
+
graphicGapMatch: [
|
|
62
|
+
"tests/browser/player-graphic-gap-match.spec.ts",
|
|
63
|
+
"tests/browser/player-graphic.spec.ts",
|
|
64
|
+
],
|
|
65
|
+
graphicOrder: ["tests/browser/player-graphic.spec.ts"],
|
|
66
|
+
hotspot: ["tests/browser/player-graphic.spec.ts"],
|
|
67
|
+
hottext: ["tests/browser/player-hottext.spec.ts", "tests/browser/player-dom-behavior.spec.ts"],
|
|
68
|
+
inlineChoice: ["tests/browser/player-inline-choice.spec.ts"],
|
|
69
|
+
match: ["tests/browser/player-match.spec.ts", "tests/browser/player-dom-behavior.spec.ts"],
|
|
70
|
+
media: ["tests/browser/player-media.spec.ts"],
|
|
71
|
+
order: [...browserKeyboardA11yTests, ...browserLifecycleTests],
|
|
72
|
+
portableCustom: ["tests/browser/player-portable-custom.spec.ts"],
|
|
73
|
+
positionObject: ["tests/browser/player-graphic.spec.ts", ...browserKeyboardA11yTests],
|
|
74
|
+
selectPoint: ["tests/browser/player-graphic.spec.ts"],
|
|
75
|
+
slider: browserKeyboardA11yTests,
|
|
76
|
+
textEntry: ["tests/browser/player-dom-behavior.spec.ts"],
|
|
77
|
+
upload: ["tests/browser/player-dom-behavior.spec.ts"],
|
|
78
|
+
};
|
|
79
|
+
return [...base, ...(extras[interactionType] ?? [])];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function interactionSupportFixtures(interactionType: QtiInteractionType): string[] {
|
|
83
|
+
return [
|
|
84
|
+
`packages/fixtures/xml/${interactionType}-reference.xml`,
|
|
85
|
+
...(interactionExtraFixtures[interactionType] ?? []),
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function interactionSupportTests(interactionType: QtiInteractionType): string[] {
|
|
90
|
+
if (interactionType === "extendedText" || interactionType === "textEntry") {
|
|
91
|
+
return [...browserTestsFor(interactionType), "packages/core/src/pattern-mask.test.ts"];
|
|
92
|
+
}
|
|
93
|
+
return browserTestsFor(interactionType);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isProcessingBrowserEvidenceName(
|
|
97
|
+
qtiName: string,
|
|
98
|
+
): qtiName is ProcessingBrowserEvidenceName {
|
|
99
|
+
return Object.hasOwn(processingBrowserEvidence, qtiName);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function processingSupportTests(qtiName: string): string[] {
|
|
103
|
+
if (!isProcessingBrowserEvidenceName(qtiName)) {
|
|
104
|
+
return [coreProcessingTest];
|
|
105
|
+
}
|
|
106
|
+
return [coreProcessingTest, ...processingBrowserEvidence[qtiName]];
|
|
107
|
+
}
|
package/src/support.ts
CHANGED
|
@@ -8,6 +8,13 @@ import type {
|
|
|
8
8
|
QtiProcessingElementSupport,
|
|
9
9
|
QtiSourceLocation,
|
|
10
10
|
} from "./types.js";
|
|
11
|
+
import {
|
|
12
|
+
browserFeedbackTests,
|
|
13
|
+
browserHarnessTests,
|
|
14
|
+
interactionSupportFixtures,
|
|
15
|
+
interactionSupportTests,
|
|
16
|
+
processingBrowserEvidence,
|
|
17
|
+
} from "./support-evidence.js";
|
|
11
18
|
|
|
12
19
|
export const interactionSupport: QtiInteractionElementSupport[] = [
|
|
13
20
|
entry("qti-associate-interaction", "associate"),
|
|
@@ -51,23 +58,36 @@ export const deprecatedInteractionSupport: QtiInteractionElementSupport[] = [
|
|
|
51
58
|
];
|
|
52
59
|
|
|
53
60
|
export const processingSupport: QtiProcessingElementSupport[] = [
|
|
54
|
-
processingEntry(
|
|
55
|
-
"
|
|
56
|
-
"packages/
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
61
|
+
processingEntry(
|
|
62
|
+
"qti-template-processing",
|
|
63
|
+
"packages/core/src/core.test.ts",
|
|
64
|
+
[
|
|
65
|
+
"packages/fixtures/xml/template-processing-reference.xml",
|
|
66
|
+
"packages/fixtures/xml/random-integer-template-reference.xml",
|
|
67
|
+
"packages/fixtures/xml/template-content-reference.xml",
|
|
68
|
+
],
|
|
69
|
+
[...processingBrowserEvidence["qti-template-processing"]],
|
|
70
|
+
),
|
|
71
|
+
processingEntry(
|
|
72
|
+
"qti-response-processing",
|
|
73
|
+
"packages/core/src/core.test.ts",
|
|
74
|
+
[
|
|
75
|
+
"packages/fixtures/xml/mapping-processing-reference.xml",
|
|
76
|
+
"packages/fixtures/xml/generic-match-processing-reference.xml",
|
|
77
|
+
"packages/fixtures/xml/advanced-processing-reference.xml",
|
|
78
|
+
"packages/fixtures/xml/adaptive-feedback-reference.xml",
|
|
79
|
+
],
|
|
80
|
+
[...processingBrowserEvidence["qti-response-processing"]],
|
|
81
|
+
),
|
|
64
82
|
processingEntry("qti-set-template-value", "packages/core/src/core.test.ts", [
|
|
65
83
|
"packages/fixtures/xml/template-processing-reference.xml",
|
|
84
|
+
"packages/fixtures/xml/random-integer-template-reference.xml",
|
|
66
85
|
"packages/fixtures/xml/template-content-reference.xml",
|
|
67
86
|
]),
|
|
68
87
|
processingEntry("qti-set-default-value", "packages/core/src/core.test.ts"),
|
|
69
88
|
processingEntry("qti-set-correct-response", "packages/core/src/core.test.ts", [
|
|
70
89
|
"packages/fixtures/xml/template-processing-reference.xml",
|
|
90
|
+
"packages/fixtures/xml/random-integer-template-reference.xml",
|
|
71
91
|
]),
|
|
72
92
|
processingEntry("qti-template-condition", "packages/core/src/core.test.ts"),
|
|
73
93
|
processingEntry("qti-template-if", "packages/core/src/core.test.ts"),
|
|
@@ -119,11 +139,17 @@ export const processingSupport: QtiProcessingElementSupport[] = [
|
|
|
119
139
|
processingEntry("qti-variable", "packages/core/src/core.test.ts", [
|
|
120
140
|
"packages/fixtures/xml/generic-match-processing-reference.xml",
|
|
121
141
|
"packages/fixtures/xml/template-processing-reference.xml",
|
|
142
|
+
"packages/fixtures/xml/random-integer-template-reference.xml",
|
|
122
143
|
"packages/fixtures/xml/template-content-reference.xml",
|
|
123
144
|
"packages/fixtures/xml/advanced-processing-reference.xml",
|
|
124
145
|
"packages/fixtures/xml/adaptive-feedback-reference.xml",
|
|
125
146
|
]),
|
|
126
|
-
processingEntry(
|
|
147
|
+
processingEntry(
|
|
148
|
+
"qti-random-integer",
|
|
149
|
+
"packages/core/src/core.test.ts",
|
|
150
|
+
["packages/fixtures/xml/random-integer-template-reference.xml"],
|
|
151
|
+
[...processingBrowserEvidence["qti-random-integer"]],
|
|
152
|
+
),
|
|
127
153
|
processingEntry("qti-random-float", "packages/core/src/core.test.ts"),
|
|
128
154
|
processingEntry("qti-random", "packages/core/src/core.test.ts"),
|
|
129
155
|
processingEntry("qti-multiple", "packages/core/src/core.test.ts", [
|
|
@@ -139,8 +165,11 @@ export const processingSupport: QtiProcessingElementSupport[] = [
|
|
|
139
165
|
]),
|
|
140
166
|
processingEntry("qti-sum", "packages/core/src/core.test.ts", [
|
|
141
167
|
"packages/fixtures/xml/template-processing-reference.xml",
|
|
168
|
+
"packages/fixtures/xml/random-integer-template-reference.xml",
|
|
169
|
+
]),
|
|
170
|
+
processingEntry("qti-product", "packages/core/src/core.test.ts", [
|
|
171
|
+
"packages/fixtures/xml/random-integer-template-reference.xml",
|
|
142
172
|
]),
|
|
143
|
-
processingEntry("qti-product", "packages/core/src/core.test.ts"),
|
|
144
173
|
processingEntry("qti-min", "packages/core/src/core.test.ts"),
|
|
145
174
|
processingEntry("qti-max", "packages/core/src/core.test.ts"),
|
|
146
175
|
processingEntry("qti-subtract", "packages/core/src/core.test.ts"),
|
|
@@ -205,9 +234,13 @@ export const itemMetadataSupport: QtiItemMetadataElementSupport[] = [
|
|
|
205
234
|
fixtures: [
|
|
206
235
|
"packages/fixtures/packages/basic-item-player/valid-item-only/items/tolerance-extra-features.xml",
|
|
207
236
|
],
|
|
208
|
-
tests: [
|
|
237
|
+
tests: [
|
|
238
|
+
"packages/core/src/core.test.ts",
|
|
239
|
+
"packages/core/src/parser-item-metadata.test.ts",
|
|
240
|
+
...browserHarnessTests,
|
|
241
|
+
],
|
|
209
242
|
notes:
|
|
210
|
-
"Parsed by parser-item-metadata.ts and validated by validateCatalogInfo. Duplicate containers emit item.child.duplicate.",
|
|
243
|
+
"Parsed by parser-item-metadata.ts and validated by validateCatalogInfo. Duplicate containers emit item.child.duplicate. Manual harness debugger coverage is in player-harness.spec.ts.",
|
|
211
244
|
},
|
|
212
245
|
{
|
|
213
246
|
qtiName: "qti-stylesheet",
|
|
@@ -225,6 +258,7 @@ export const itemMetadataSupport: QtiItemMetadataElementSupport[] = [
|
|
|
225
258
|
"packages/core/src/core.test.ts",
|
|
226
259
|
"packages/core/src/parser-item-metadata.test.ts",
|
|
227
260
|
"tests/browser/player-package.spec.ts",
|
|
261
|
+
...browserHarnessTests,
|
|
228
262
|
],
|
|
229
263
|
notes:
|
|
230
264
|
"Parsed by parser-item-metadata.ts, validated by validateStylesheets, and delivered by the player only when a host resolveStylesheet hook returns a safe resolved stylesheet URL.",
|
|
@@ -239,9 +273,13 @@ export const itemMetadataSupport: QtiItemMetadataElementSupport[] = [
|
|
|
239
273
|
render: false,
|
|
240
274
|
process: false,
|
|
241
275
|
fixtures: ["packages/fixtures/src/index.ts"],
|
|
242
|
-
tests: [
|
|
276
|
+
tests: [
|
|
277
|
+
"packages/core/src/core.test.ts",
|
|
278
|
+
"packages/core/src/parser-item-metadata.test.ts",
|
|
279
|
+
...browserFeedbackTests,
|
|
280
|
+
],
|
|
243
281
|
notes:
|
|
244
|
-
"Parsed by parser-item-metadata.ts and validated by validateModalFeedback. Player
|
|
282
|
+
"Parsed by parser-item-metadata.ts and validated by validateModalFeedback. Player renders outcome-gated modal feedback in player-feedback.spec.ts.",
|
|
245
283
|
},
|
|
246
284
|
{
|
|
247
285
|
qtiName: "qti-companion-materials-info",
|
|
@@ -258,6 +296,7 @@ export const itemMetadataSupport: QtiItemMetadataElementSupport[] = [
|
|
|
258
296
|
tests: [
|
|
259
297
|
"packages/core/src/parser-companion-materials.test.ts",
|
|
260
298
|
"packages/core/src/parser-item-metadata.test.ts",
|
|
299
|
+
...browserHarnessTests,
|
|
261
300
|
],
|
|
262
301
|
notes:
|
|
263
302
|
"Parses qti-physical-material text and qti-digital-material file references. Digital materials require non-empty qti-file-href text and may include label, mime-type, and qti-resource-icon metadata. Hosts read resolved materials through createCompanionMaterialsResolution() or player.getCompanionMaterialsResolution().",
|
|
@@ -364,33 +403,14 @@ function entry(qtiName: string, interactionType: QtiInteractionType): QtiInterac
|
|
|
364
403
|
validate: true,
|
|
365
404
|
render: true,
|
|
366
405
|
process: true,
|
|
367
|
-
fixtures:
|
|
368
|
-
tests:
|
|
369
|
-
"packages/fixtures/src/fixtures.test.ts",
|
|
370
|
-
"packages/conformance/src/conformance.test.ts",
|
|
371
|
-
"packages/a11y/src/a11y.test.ts",
|
|
372
|
-
"tests/browser/player.spec.ts",
|
|
373
|
-
],
|
|
406
|
+
fixtures: interactionSupportFixtures(interactionType),
|
|
407
|
+
tests: interactionSupportTests(interactionType),
|
|
374
408
|
};
|
|
375
409
|
}
|
|
376
410
|
|
|
377
411
|
function extendedTextInteractionEntry(): QtiInteractionElementSupport {
|
|
378
412
|
return {
|
|
379
413
|
...entry("qti-extended-text-interaction", "extendedText"),
|
|
380
|
-
fixtures: [
|
|
381
|
-
"packages/fixtures/xml/extendedText-reference.xml",
|
|
382
|
-
"packages/fixtures/packages/sv-matrix/items/extended-text-pattern-mask.xml",
|
|
383
|
-
"packages/fixtures/packages/sv-matrix/items/extended-text-xhtml.xml",
|
|
384
|
-
],
|
|
385
|
-
tests: [
|
|
386
|
-
"packages/fixtures/src/fixtures.test.ts",
|
|
387
|
-
"packages/conformance/src/conformance.test.ts",
|
|
388
|
-
"packages/a11y/src/a11y.test.ts",
|
|
389
|
-
"packages/core/src/pattern-mask.test.ts",
|
|
390
|
-
"tests/browser/player.spec.ts",
|
|
391
|
-
"tests/browser/player-dom-behavior.spec.ts",
|
|
392
|
-
"tests/browser/player-extended-text-xhtml.spec.ts",
|
|
393
|
-
],
|
|
394
414
|
notes: "Supports plain and format=xhtml extended text.",
|
|
395
415
|
};
|
|
396
416
|
}
|
|
@@ -398,18 +418,6 @@ function extendedTextInteractionEntry(): QtiInteractionElementSupport {
|
|
|
398
418
|
function textEntryInteractionEntry(): QtiInteractionElementSupport {
|
|
399
419
|
return {
|
|
400
420
|
...entry("qti-text-entry-interaction", "textEntry"),
|
|
401
|
-
fixtures: [
|
|
402
|
-
"packages/fixtures/xml/textEntry-reference.xml",
|
|
403
|
-
"packages/fixtures/packages/sv-matrix/items/text-entry-pattern-mask-inline.xml",
|
|
404
|
-
],
|
|
405
|
-
tests: [
|
|
406
|
-
"packages/fixtures/src/fixtures.test.ts",
|
|
407
|
-
"packages/conformance/src/conformance.test.ts",
|
|
408
|
-
"packages/a11y/src/a11y.test.ts",
|
|
409
|
-
"packages/core/src/pattern-mask.test.ts",
|
|
410
|
-
"tests/browser/player.spec.ts",
|
|
411
|
-
"tests/browser/player-dom-behavior.spec.ts",
|
|
412
|
-
],
|
|
413
421
|
notes: "Supports placeholder-text and pattern-mask on text-entry controls.",
|
|
414
422
|
};
|
|
415
423
|
}
|
|
@@ -426,6 +434,7 @@ function processingEntry(
|
|
|
426
434
|
qtiName: string,
|
|
427
435
|
test: string,
|
|
428
436
|
fixtures: string[] = [],
|
|
437
|
+
extraTests: string[] = [],
|
|
429
438
|
): QtiProcessingElementSupport {
|
|
430
439
|
return {
|
|
431
440
|
qtiName,
|
|
@@ -437,6 +446,6 @@ function processingEntry(
|
|
|
437
446
|
render: false,
|
|
438
447
|
process: true,
|
|
439
448
|
fixtures,
|
|
440
|
-
tests: [test],
|
|
449
|
+
tests: [test, ...extraTests],
|
|
441
450
|
};
|
|
442
451
|
}
|
|
@@ -107,6 +107,7 @@ export function runTrustedItemSession(
|
|
|
107
107
|
parsedResult.parsed,
|
|
108
108
|
input.priorState,
|
|
109
109
|
input.diagnosticPrefix,
|
|
110
|
+
input.allowedUndeclaredResponseIdentifiers,
|
|
110
111
|
parsedResult.parsed.diagnostics,
|
|
111
112
|
);
|
|
112
113
|
if (!sessionResult.ok) {
|
|
@@ -127,9 +128,7 @@ export function runTrustedItemSession(
|
|
|
127
128
|
return emptyTrustedItemSessionFailure(diagnostics);
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
const shouldScore =
|
|
131
|
-
input.scoring === "always" ||
|
|
132
|
-
(input.scoring === "onSubmission" && applicationResult.appliedSubmission);
|
|
131
|
+
const shouldScore = input.scoring === "always" || applicationResult.appliedSubmission;
|
|
133
132
|
|
|
134
133
|
let outcomes = sessionResult.session.serialize().outcomes;
|
|
135
134
|
let state = sessionResult.session.serialize();
|
|
@@ -254,12 +253,15 @@ function createTrustedItemSession(
|
|
|
254
253
|
parsed: QtiParsedTrustedItem,
|
|
255
254
|
priorState: QtiAttemptStateV1 | null | undefined,
|
|
256
255
|
diagnosticPrefix: QtiTrustedInputDiagnosticPrefix,
|
|
256
|
+
allowedUndeclaredResponseIdentifiers: readonly string[] | undefined,
|
|
257
257
|
existingDiagnostics: QtiDiagnostic[] = [],
|
|
258
258
|
): { ok: true; session: QtiItemSession } | { ok: false; diagnostics: QtiDiagnostic[] } {
|
|
259
259
|
try {
|
|
260
260
|
return {
|
|
261
261
|
ok: true,
|
|
262
|
-
session: createItemSession(parsed.document, priorState ?? undefined
|
|
262
|
+
session: createItemSession(parsed.document, priorState ?? undefined, {
|
|
263
|
+
allowedUndeclaredResponseIdentifiers,
|
|
264
|
+
}),
|
|
263
265
|
};
|
|
264
266
|
} catch (error) {
|
|
265
267
|
return {
|
package/src/tts.ts
CHANGED
|
@@ -103,10 +103,17 @@ interface SegmentInput {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
const dataSsmlFunctionNames = new Set(["break", "phoneme", "prosody", "say-as", "sub"]);
|
|
106
|
-
const breakStrengths = new Set([
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
const breakStrengths = new Set<QtiDataSsmlBreakStrength>([
|
|
107
|
+
"medium",
|
|
108
|
+
"none",
|
|
109
|
+
"strong",
|
|
110
|
+
"weak",
|
|
111
|
+
"x-strong",
|
|
112
|
+
"x-weak",
|
|
113
|
+
]);
|
|
114
|
+
const phonemeAlphabets = new Set<NonNullable<QtiDataSsmlPhoneme["alphabet"]>>(["ipa", "x-sampa"]);
|
|
115
|
+
const phonemeTypes = new Set<NonNullable<QtiDataSsmlPhoneme["type"]>>(["default", "ruby"]);
|
|
116
|
+
const sayAsInterpretations = new Set<QtiDataSsmlSayAs["interpret-as"]>([
|
|
110
117
|
"cardinal",
|
|
111
118
|
"characters",
|
|
112
119
|
"date",
|
|
@@ -200,7 +207,7 @@ function validateBreak(value: Record<string, unknown>, errors: string[]): QtiDat
|
|
|
200
207
|
const result: QtiDataSsmlBreak = {};
|
|
201
208
|
const strength = optionalEnum(value, "strength", "break.strength", breakStrengths, errors);
|
|
202
209
|
const time = optionalString(value, "time", "break.time", errors);
|
|
203
|
-
if (strength) result.strength = strength
|
|
210
|
+
if (strength) result.strength = strength;
|
|
204
211
|
if (time !== undefined) result.time = time;
|
|
205
212
|
return result;
|
|
206
213
|
}
|
|
@@ -215,8 +222,8 @@ function validatePhoneme(
|
|
|
215
222
|
const type = optionalEnum(value, "type", "phoneme.type", phonemeTypes, errors);
|
|
216
223
|
if (!ph) return undefined;
|
|
217
224
|
const result: QtiDataSsmlPhoneme = { ph };
|
|
218
|
-
if (alphabet) result.alphabet = alphabet
|
|
219
|
-
if (type) result.type = type
|
|
225
|
+
if (alphabet) result.alphabet = alphabet;
|
|
226
|
+
if (type) result.type = type;
|
|
220
227
|
return result;
|
|
221
228
|
}
|
|
222
229
|
|
|
@@ -241,7 +248,7 @@ function validateSayAs(
|
|
|
241
248
|
errors,
|
|
242
249
|
);
|
|
243
250
|
if (!interpretation) return undefined;
|
|
244
|
-
return { "interpret-as": interpretation
|
|
251
|
+
return { "interpret-as": interpretation };
|
|
245
252
|
}
|
|
246
253
|
|
|
247
254
|
function validateSub(value: Record<string, unknown>, errors: string[]): QtiDataSsmlSub | undefined {
|
|
@@ -520,29 +527,36 @@ function requiredString(
|
|
|
520
527
|
return optionalString(value, name, path, errors);
|
|
521
528
|
}
|
|
522
529
|
|
|
523
|
-
function
|
|
530
|
+
function isAllowedEnum<T extends string>(value: string, allowed: ReadonlySet<T>): value is T {
|
|
531
|
+
for (const entry of allowed) {
|
|
532
|
+
if (entry === value) return true;
|
|
533
|
+
}
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function optionalEnum<T extends string>(
|
|
524
538
|
value: Record<string, unknown>,
|
|
525
539
|
name: string,
|
|
526
540
|
path: string,
|
|
527
|
-
allowed:
|
|
541
|
+
allowed: ReadonlySet<T>,
|
|
528
542
|
errors: string[],
|
|
529
|
-
):
|
|
543
|
+
): T | undefined {
|
|
530
544
|
const property = optionalString(value, name, path, errors);
|
|
531
545
|
if (property === undefined) return undefined;
|
|
532
|
-
if (!
|
|
546
|
+
if (!isAllowedEnum(property, allowed)) {
|
|
533
547
|
errors.push(`${path} has unsupported value "${property}".`);
|
|
534
548
|
return undefined;
|
|
535
549
|
}
|
|
536
550
|
return property;
|
|
537
551
|
}
|
|
538
552
|
|
|
539
|
-
function requiredEnum(
|
|
553
|
+
function requiredEnum<T extends string>(
|
|
540
554
|
value: Record<string, unknown>,
|
|
541
555
|
name: string,
|
|
542
556
|
path: string,
|
|
543
|
-
allowed:
|
|
557
|
+
allowed: ReadonlySet<T>,
|
|
544
558
|
errors: string[],
|
|
545
|
-
):
|
|
559
|
+
): T | undefined {
|
|
546
560
|
if (!Object.hasOwn(value, name)) {
|
|
547
561
|
errors.push(`${path} is required.`);
|
|
548
562
|
return undefined;
|