pediatric-ai-scribe-v3/test/theme-preview-visible.test.js
Daniel 8602c7bd14
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 46s
Forgejo Docker Build / Root app tests (push) Successful in 57s
Forgejo Android APK / Build signed APK (push) Successful in 2m4s
Forgejo Docker Build / Build Docker image (push) Successful in 19s
Forgejo Docker Build / Deploy to the host (push) Failing after 1s
fix: the theme preview says what it is doing, and says when it fails
The box was hidden until an image loaded and hidden again on error, so a
preview that failed and a preview that did not exist looked identical —
an empty gap under the dropdown. That is what it looked like from the
outside, and it cost a diagnosis: the endpoint renders a correct 934x525
PNG in 0.66s when called exactly as the browser calls it, cookie and
all, and nothing on the page could have told anyone that.

The first view of a theme is a pptx render, a Gotenberg round trip and a
pdftoppm — a second or so before anything appears, and then cached on
disk. A second of silence reads as a feature that is not there, so it
now says it is drawing, and on failure says the look itself is
unaffected, which is true: the preview is a picture of the export, not
the thing that produces it.

img.src is assigned after both handlers. A cached preview can complete
before a handler attached below the assignment exists, which would leave
the box stuck on "Drawing...".

describeTheme already ends by calling it, so the call sitting beside it
fired a second identical request on every load.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 21:56:22 +02:00

60 lines
2.9 KiB
JavaScript

// The theme preview box was hidden on error and hidden until an image loaded,
// so a preview that failed and a preview that did not exist looked exactly the
// same — an empty gap. That cost a diagnosis: the endpoint was working the
// whole time and there was no way to tell from the page.
//
// The first view of a theme is a pptx render, a Gotenberg round trip and a
// pdftoppm, so it is a second or so before anything appears. Silence for that
// second reads as a missing feature.
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const read = f => fs.readFileSync(path.join(__dirname, '..', f), 'utf8');
const ui = read('public/js/myResources.js');
const fn = ui.slice(ui.indexOf('function showThemePreview'), ui.indexOf('function describeTheme'));
test('a failed preview says so instead of vanishing', () => {
assert.doesNotMatch(fn, /onerror = function \(\) \{ box\.hidden = true; \}/);
assert.match(fn, /img\.onerror = function \(\)[\s\S]{0,200}note\.textContent = 'No preview just now/);
});
test('it says it is working while the render happens', () => {
assert.match(fn, /note\.textContent = 'Drawing a sample slide\.\.\.'/);
assert.match(fn, /box\.hidden = false/);
});
test('the note is replaced by the image, not left stacked above it', () => {
assert.match(fn, /img\.onload = function \(\) \{ img\.style\.display = 'block'; note\.hidden = true; \}/);
});
test('src is assigned after the handlers, so a cache hit is not missed', () => {
// A cached preview can complete before a handler attached after the
// assignment would exist, leaving the box stuck on "Drawing...".
const onload = fn.indexOf('img.onload');
const onerror = fn.indexOf('img.onerror');
const src = fn.indexOf('img.src =');
assert.ok(onload > -1 && onerror > -1 && src > -1);
assert.ok(src > onload && src > onerror, 'img.src must be set after both handlers');
});
test('no request for a theme that has not been chosen', () => {
assert.match(fn, /if \(!select \|\| !select\.value \|\| !box\) return;/);
});
test('the preview is requested once per change, not twice', () => {
// describeTheme ends by calling it; a second call beside it fired an
// identical request on every load.
assert.equal((ui.match(/\n\s*showThemePreview\(\);/g) || []).length, 1);
});
test('the route it calls exists and is the one that renders', () => {
const route = read('src/routes/myResources.js');
assert.match(fn, /'\/api\/my-resources\/theme-preview\/' \+ encodeURIComponent\(select\.value\)/);
assert.match(route, /router\.get\('\/my-resources\/theme-preview\/:id'/);
// It must be registered before the '/my-resources/:id' catch-all, or that
// would answer instead.
assert.ok(route.indexOf("'/my-resources/theme-preview/:id'") < route.indexOf("router.get('/my-resources/:id'"),
'theme-preview must be registered before the :id route');
});