Skip to content

[QTI] Text entry question with an image in its prompt fails to save #6267

Closed

Description

@AlexVelezLl

❌ This issue is not open for contribution. Visit Contributing guidelines to learn about the contributing process and how to find suitable issues.

Target branch: unstable

Observed behavior

Adding an image or small text to the prompt of a text entry question (numeric, text entry, free response) produces an item the server rejects, so the question doesn't save.

  • TipTap emits images and small text as top-level <img> and <small>.
  • buildTextEntryInteractionXML writes the prompt directly into <qti-item-body>, which the QTI schema only allows to hold block elements.
  • The sync API runs validate_qti_item on every QTI write and rejects the item.

Inline choice has the same constraint and already handles it with BLOCK_TAGS / wrapInlineRuns in interactions/inlineChoice/parse.js, but only for itself. Every other interaction writes TipTap content into qti-prompt, qti-simple-choice, qti-simple-associable-choice or qti-html-content, all of which accept it.

Errors and logs

Schema check on an item body with a top-level image, as the text entry builder writes it:

Element '{http://www.imsglobal.org/xsd/imsqtiasi_v3p0}img': This element is not expected.

Expected behavior

Any TipTap content in a text entry prompt saves and reopens unchanged. Every interaction that writes authored HTML directly into <qti-item-body> goes through a single shared helper, instead of each one handling the rule itself:

  • The inline choice wrapping moves out of inlineChoice/parse.js into a shared helper in serialization/xml.js that turns an HTML fragment into a list of block elements for <qti-item-body>.
  • Builders call it where they place content, before setting any markers: inline choice for its prompt and passage (its data-studio-prompt marker must stay on a top-level element), text entry for its prompt.

User-facing consequences

Authors can't add an image or small text to the prompt of a numeric, text entry or free response question. The save fails with a sync error.

Steps to reproduce

  1. Open an exercise and add a numeric or text entry question.
  2. Add an image to the question prompt.
  3. Save. The item is rejected by the sync API's QTI validation.

Acceptance Criteria

  • A text entry prompt containing an image, small text, or both saves, passes validate_qti_item, and reopens with the same content
  • Inline choice keeps its current behavior, including prompt/passage separation for prompts with images or small text
  • Inline choice and text entry use the same helper from serialization/xml.js; inlineChoice/parse.js no longer defines its own
  • Tests cover a text entry prompt with an image and with small text, built and parsed back

Context

Alternatives considered:

  • Wrap in assembleItemXml: covers every interaction without builders calling anything, but it runs after builders set their markers. Inline choice would tag a bare <img data-studio-prompt>, assembly would wrap it in an unmarked <div>, and on reopen the image would move from the prompt to the passage.
  • Make TipTap emit only block elements at the root: only fixes TipTap's output, not stored or imported XML that reaches the inline choice parser. It also changes the stored markup of every rich-text field where a top-level <img> is already valid, and Image and Small would have to parse both the old and new forms permanently.

AI usage

I used Claude Code to check TipTap's output and every interaction's content slot against the server QTI schema, compare the alternatives, and draft this issue. I reviewed the findings and edited the draft. The save failure is based on the schema check, not reproduced in the app.

Activity

  1. added theissue type on Oct 1, 2026
  2. changed the title [-]Text entry question with an image in its prompt fails to save[/-] [+][QTI] Text entry question with an image in its prompt fails to save[/+] on Oct 1, 2026
  3. rtibblesbot commented on Oct 1, 2026

    @rtibblesbot
    Contributor

    Plan: Text entry prompts with images or small text save past validate_qti_item, via one shared <qti-item-body> block-wrapping helper.

    • Shared block-wrapping helper in xml.js
    • Text entry prompt goes through the helper
    • Self-review the produced code

    Assumptions — the issue did not settle these; say so if one is wrong.

    • Text entry's extractPromptHTML keeps XML innerHTML; if reviewers want HTML-form output (<img> not <img/>), switch it to serializeAsHtml as inline choice does.

    @rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly

    How was this generated?
    • Implemented code following the pre-approved plan using test-driven development (red-green-refactor)
    • Ran the project's full test suite and linter before committing
    • For UI changes: captured live screenshots to verify rendering
    • For backend changes: verified with example API/CLI output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions