Repository navigation
[QTI] Consolidate QTIEditor DOM parsing on parseXML and support text/html #6044
Description
❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.
Overview
parseXML in QTIEditor/serialization/parseItem.js already wraps DOMParser with the one thing raw DOMParser doesn't give you: a real failure signal. DOMParser never throws on malformed input — it returns a document containing a <parsererror> node — so every call site that skips that check silently proceeds with a broken document.
Most QTIEditor code doesn't go through parseXML. It instantiates new DOMParser() directly, so the parsererror check is inconsistently applied and the parser setup is duplicated across the module. Consolidating on parseXML makes malformed-XML handling uniform and gives us one place to change parsing behavior.
parseXML currently hardcodes 'text/xml', but several call sites legitimately need 'text/html' (HTML fragment assembly, sanitization). It needs to accept the mime type to be usable by them.
Complexity: Low
Target branch: unstable
Context
parseXML (serialization/parseItem.js:13) parses as 'text/xml' and throws on a parsererror node. Direct DOMParser use in QTIEditor today:
Production code
| File | Line | Parsed as | Notes |
|---|---|---|---|
interactions/choice/ChoiceInteractionDescriptor.js |
31 | text/xml |
no parsererror check |
serialization/assembleItem.js |
41 | text/html |
HTML fragment assembly |
serialization/assembleItem.js |
83, 88, 93 | text/xml |
separate declParser instance |
serialization/qti/QTISanitizer.js |
94 | text/html |
Tests and helpers
interactions/choice/__tests__/ChoiceInteractionDescriptor.spec.js (16, 28, 40), interactions/choice/__tests__/parse.spec.js (149), serialization/__tests__/assembleItem.spec.js (79, 155, 174, 186, 215, 228, 236, 247), serialization/qti/__tests__/testUtils.js (4).
One wrinkle worth knowing before starting: parsererror detection is XML-only. The HTML parser never fails — it recovers silently — so a text/html parse would never produce a parsererror node, and an HTML document that happens to contain an element named parsererror would trip a naive check. The error check has to apply only to XML parsing.
The Change
Consolidate QTIEditor's XML and HTML parsing onto parseXML so that parser setup and malformed-input handling live in one place.
parseXML gains a mime type parameter, defaulting to 'text/xml' so existing callers are unaffected. Callers that need HTML fragment semantics pass 'text/html'. The parsererror check applies only when parsing as XML, since the HTML parser never signals failure that way.
parseXML stays in serialization/parseItem.js; call sites import it from there.
Every direct new DOMParser() in QTIEditor — production code and tests alike — routes through parseXML instead. Call sites that currently skip the parsererror check inherit it, which is the point: ChoiceInteractionDescriptor.js:31 parsing a response declaration with no validation is exactly the case where a malformed document should surface rather than propagate.
pasteTransform.js in TipTapEditor is left alone. It's outside QTIEditor, and pointing a shared editor at QTIEditor/serialization/ would work against #5970's goal of keeping QTIEditor extractable as a standalone package.
Out of Scope
TipTapEditor/utils/pasteTransform.js:78— outside QTIEditor; converting it would create a cross-feature import intoQTIEditor/serialization/.serialization/assembleItem.js:11— parses'<root/>'as a document factory forcreateElementrather than to parse real input; leave as-is.- Moving
parseXMLto a different module. It stays inparseItem.js.
Acceptance Criteria
General
-
parseXMLaccepts a mime type argument, defaulting to'text/xml'so existing callers are unchanged - The
parsererrorcheck runs only when parsing as XML;'text/html'parses leniently without throwing -
parseXMLremains exported fromserialization/parseItem.js - QTIEditor parses through
parseXMLexcept where a bareDocumentis needed as an element factory -
ChoiceInteractionDescriptor.js:31,assembleItem.js:41,assembleItem.js:83/88/93, andqti/QTISanitizer.js:94all parse viaparseXML, each passing the mime type it needs - Call sites that previously parsed without a
parsererrorcheck now surface malformed input as a thrown error - QTIEditor tests and test helpers parse via
parseXMLrather than constructing their ownDOMParser
Testing
-
parseXMLunit tests cover: default XML parsing, explicit'text/xml','text/html', malformed XML throwing, and malformed HTML not throwing - Existing QTIEditor serialization and interaction tests pass unchanged
References
- Follow-up from implement choice interaction parsing and serialization for QTIEditor #6012, which introduced the QTIEditor serialization layer
parseXML—contentcuration/contentcuration/frontend/shared/views/QTIEditor/serialization/parseItem.js
AI usage
This issue was drafted with Claude Code (Opus 4.8) from a code review of #6012. I described the change I wanted; Claude surveyed the DOMParser call sites, surfaced the XML-vs-HTML error-handling asymmetry, and drafted the sections. I reviewed and corrected the scope and criteria before filing.

Activity
rtibblesbot commentedon Jul 16, 2026
Plan: Give
parseXMLa mime-type parameter (XML-onlyparsererrorcheck) and route every directnew DOMParser()in QTIEditor through it, except the one bare-Documentelement factory.parseXMLgains a mime-type parameter with an XML-only error checkQTISanitizer.stripTagsthroughparseXML('text/html')testUtils.jsthrough the sharedparseXMLDOMParser, whole suite green@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?
rtibblesbot commentedon Jul 16, 2026
The bot has been waiting for a response for 2 hour(s). It will resume automatically when someone replies.