Skip to content

feat: play every content type in the library pack (pnpm sweep) - #21

Merged
alekswebnet merged 2 commits into
mainfrom
feat/content-type-sweep
Oct 13, 2026
Merged

alekswebnet merged 2 commits into
mainfrom
feat/content-type-sweep

Conversation

@alekswebnet

@alekswebnet alekswebnet commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes #20.

pnpm sweep plays every runnable library in the library pack as a content-only export, untrusted and trusted, and fails when one doesn't reach ready or throws while booting. There are 55 libraries: the hub's 53 content types, plus two the pack carries as options of others. That makes 110 plays, which take about three minutes locally and five on CI.

Results

All 110 plays boot. The report also lists what each content type sent when the generic walk pressed its buttons; that part is information, not a check (see below).

Measured once, with the first cut of this PR, which worked each type through with hand-written steps:

Content types
End with their own answered 19: the question types
End with their own completed 13: Agamotto, Branching Scenario, Column, Course Presentation, Documentation Tool, Game Map, Image Pair, Interactive Book, Interactive Video, Memory Game, Question Set, Questionnaire, Single Choice Set
Send no result (confirmed in their code) 19, such as Accordion, Dialog Cards, Personality Quiz, Image Slider
Can't be worked through headless: need a microphone, speech recognition or a camera 4

For Interactive Book, Course Presentation and Interactive Video, which #16 never played, the parts' statements carry a parent and the content's own completed is the result.

Two content types report a part's result as their own

  • Interactive Book 1.15 creates each chapter with { parent: self }. In that scope self is the global window, not the book, so every chapter's completed has no parent. The book's own completed comes only when its report is submitted, and the submit button appears only with reporting. Without reporting, a book's last finished is its last chapter's.
  • Game Map 1.5 sends a stage's question's answered with no parent. The map's own completed comes when the learner finishes the map.

H5P's core uses the same rule for setFinished, so both behave this way on any H5P site. It matters for hosts: one that takes the last finished gets the content's result (for a book, with reporting); cmi5 records the first, so a unit with either completes on its first part. Both are now in the README's event table and docs/cmi5.md.

What was cut, and why

The first cut had a step file per content type (click, drag, fill, timed pauses — 13 files on the libraries' own CSS selectors) and failed a play whose last finished wasn't the one its file named. Game Map's seven-click finish passed four runs locally and failed both modes on the first CI run, on the slower runner. So the steps and the finished gate went, the same day:

  • 55 files → 10. Four are needed for the content type to play at all (Drag and Drop's drop zones, a crossword's crossing words, a branching scenario's node graph, a game map's stage); six give a container or question a real right answer so the report shows a real result. None has selectors or timing.
  • The script keeps the generic walk and reports what it reached. What stays as a failure is deterministic: not ready, or an error while booting.

How it works

Generator (apps/demo/scripts/lib/sweep-content.mjs): content from the content type's own semantics.json, the way H5P's editor saves a form — every default, the smallest valid value for a required field without one, an untouched optional group with its defaults only, a library field from the first option the pack has. tests/sweep-content.test.ts holds the rules, 12 tests.

Files (apps/demo/sweep/<machineName>.json): params, walked with the semantics so everything left out is filled in; attributes for the element (reporting). pnpm sweep --semantics <machineName> prints a content type's fields for writing one.

Script (apps/demo/scripts/sweep-content-types.mjs): plays each package as a picked file against the local pack in the runner's Chrome (Playwright's Chromium can't decode Interactive Video's H.264 clip); writes report.md, report.json, the packages and screenshots.

CI:

  • .github/workflows/content-types.yml: on PRs touching the player's source, the runtime, the pack or the sweep, and every Monday; the report is the run's summary, the packages and screenshots an artifact.
  • The weekly pack refresh (libraries.yml) sweeps the new pack and puts the report in the PR it opens, since CI doesn't start on a PR a workflow opened.

Docs: AGENTS.md (The content-type sweep, the measurements under the finished invariant, layout and commands), docs/development.md, README, docs/cmi5.md. The demo's tsconfig.json gains allowJs so its tests can import the generator, as the player's test config does.

Testing

Run Result
pnpm sweep, cut down, locally 110/110
pnpm sweep, first cut, locally, three runs 110/110 each
pnpm sweep, first cut, on CI 108/110: Game Map, both modes
tests/sweep-content.test.ts 12/12
pnpm typecheck clean

The CI sweep on this PR's latest commit is the check for the cut-down version on GitHub.

Not verified

  • Each content type plays only the one generated or written package, not everything it can hold.
  • Chromium only.
  • Whether a type's result is right: the report shows it, nothing checks it.

Release

None. Nothing published changes except two documentation lines in the README.

🤖 Generated with Claude Code

The sweep generates content for each runnable library in the pack from
its own semantics, lays in what apps/demo/sweep/<machineName>.json says
(a right answer, a drop zone, the steps through it, the finished
expected), plays it as a content-only export untrusted and trusted, and
reports every statement and finished. 110 plays, about three minutes;
CI runs it on changes to the player, the runtime, the pack or the
sweep, weekly, and in the pack refresh before its pull request.

All 55 boot in both modes. 32 end with a result of their own, 19 send
none, 4 need a microphone or camera. Interactive Book and Game Map
create their parts without a parent, so a part's result arrives as the
content's; documented for hosts and cmi5.

Refs #20

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
h5p-offline-player Ready Ready Preview Oct 13, 2026 11:06pm UTC

The first cut worked each type through with hand-written steps on the
libraries' own selectors and timing, and failed a play whose last
finished was not the one its file named. Game Map's finish passed four
runs locally and failed both modes on the first CI run. The steps and
the finished gate go: ten files stay, with content and attributes only,
and what a type sends after ready is reported, not judged.

Refs #20

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alekswebnet
alekswebnet merged commit ffe1fa1 into main Oct 13, 2026
10 checks passed
@alekswebnet
alekswebnet deleted the feat/content-type-sweep branch October 13, 2026 23:55

This branch was successfully deployed

1 active deployment
Preview — 0a1aab17 Deployed Oct 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Play every content type in the library pack, and check what each one reports

1 participant