pediatric-ai-scribe-v3/test/deck-theme-applied.test.js
Daniel 0163d40811
Some checks failed
Forgejo Docker Build / Root app tests (push) Successful in 47s
Forgejo Docker Build / Build Docker image (push) Successful in 6s
Forgejo Docker Build / End-to-end (browser) (push) Failing after 8s
fix: every presentation takes a theme, not only the ones the model designed
"Change template" answered "no slide layout" for 28 of the 41 presentations
in production: the theme lived only inside the deck JSON, and a presentation
whose deck reply failed twice and fell back to markdown slides had nowhere
to keep one. The theme is a column now, written at generation and by the
picker, and the markdown slide builder carries it to the same renderer field
a designed deck uses. A deck's own theme field is kept in step. Articles are
the only thing refused — they have no slides.

A deck reply that fails to parse is logged with its first 240 characters, so
the next "the reply was not a deck" can be read rather than guessed at.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-13 13:32:24 +02:00

65 lines
3.7 KiB
JavaScript

// A deck re-skinned in the library rendered in the default palette anyway. The
// picker saved the choice correctly and the renderer could apply it correctly;
// the theme was dropped in between, by the step that puts the drawn figures
// back onto the slides.
//
// attachFigures rebuilds the deck as a fresh object, and a fresh object keeps
// only the fields it names. It named title, subtitle, date and slides. Three
// different themes rendered byte-identical files.
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');
test('the theme survives the trip to the renderer', () => {
const src = read('src/utils/documentExport.js');
const out = src.slice(src.indexOf('var out = {'), src.indexOf('var out = {') + 220);
assert.match(out, /theme: deck\.theme/,
'attachFigures drops the theme, so every export renders in the default palette');
});
test('every field the renderer reads off the deck is carried', () => {
// The failure was one missing key in an object literal, and it was silent.
// Anything the renderer reads at deck level has to be listed there.
const src = read('src/utils/documentExport.js');
const out = src.slice(src.indexOf('var out = {'), src.indexOf('var out = {') + 220);
for (const field of ['title', 'subtitle', 'date', 'theme', 'slides']) {
assert.match(out, new RegExp(field + ':'), field + ' is not carried through attachFigures');
}
});
test('the saved theme is what the library writes, and the renderer reads', () => {
// Both ends already worked; this pins that they agree on the field name.
const route = read('src/routes/myResources.js');
assert.match(route, /deck\.theme = theme/, 'the library writes deck.theme');
const renderer = read('scripts/render_pptx.py');
assert.match(renderer, /apply_theme\(/);
assert.match(renderer, /spec\.get\("theme"\)|deck\.get\("theme"\)|data\.get\("theme"\)/,
'the renderer reads the theme off the deck');
});
test('an unknown theme leaves the default rather than failing the render', () => {
const renderer = read('scripts/render_pptx.py');
const fn = renderer.slice(renderer.indexOf('def apply_theme'));
assert.match(fn.slice(0, 400), /a deck rendering in the wrong colours beats a deck not rendering/);
});
test('a presentation that fell back to markdown slides still takes a theme', () => {
// 28 of the 41 presentations in production had no structured deck, and for
// every one of them "change template" answered that there was no layout to
// change. The theme is a column now, and the markdown slide builder carries
// it to the same renderer field a designed deck uses.
const slideSpec = require('../src/utils/slideSpec');
const spec = slideSpec.build('% Title\n% Author\n% 2026\n\n# One\n\n- a\n- b\n', { theme: 'ward-teal' });
assert.equal(spec.theme, 'ward-teal');
assert.equal(slideSpec.build('# One\n- a').theme, undefined, 'no theme asked for, none invented');
const route = fs.readFileSync(path.join(__dirname, '..', 'src/routes/myResources.js'), 'utf8');
assert.doesNotMatch(route, /has no slide layout, so it has no theme to change/);
assert.match(route, /UPDATE user_resources SET theme = \?, deck = COALESCE\(\?::jsonb, deck\)/);
assert.match(route, /deck: row\.deck, theme: row\.theme, figureIds/);
const exporter = fs.readFileSync(path.join(__dirname, '..', 'src/utils/documentExport.js'), 'utf8');
assert.match(exporter, /slideSpec\.build\(markdown, \{ images: images, theme: options\.theme \}\)/);
assert.match(fs.readFileSync(path.join(__dirname, '..', 'migrations/1781200000000_resource-theme.js'), 'utf8'), /ADD COLUMN IF NOT EXISTS theme TEXT/);
});