// A picture the deck wanted and did not get keeps its place: an empty frame // labelled with what belongs there, for the author to fill after downloading // — a real radiograph, a run chart, a photograph no model should draw. It // used to become a slide of text, which forgot that a picture was wanted. const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const deckBuild = require('../src/utils/deckBuild'); const deckSchema = require('../src/utils/deckSchema'); const docSpec = require('../src/utils/docSpec'); const read = f => fs.readFileSync(path.join(__dirname, '..', f), 'utf8'); function deck() { return deckSchema.normalise({ title: 'T', slides: [ { type: 'figure', heading: 'The film', bullets: ['a'], image_prompt: 'the admission chest radiograph' }, { type: 'image', heading: 'Run chart', image_prompt: 'the run chart of hand-hygiene compliance', caption: 'Weekly' }, { type: 'custom', heading: 'Layout', shapes: [ { kind: 'text', x: 5, y: 20, w: 40, h: 20, text: 'beside it' }, { kind: 'image', x: 50, y: 20, w: 45, h: 50, image_prompt: 'a fishbone diagram' } ] }, { type: 'bullets', heading: 'Plain', bullets: ['b'] } ] }); } test('with illustration off every requested figure stays a figure, marked as a frame to fill', async () => { const d = deck(); const result = await deckBuild.drawFigures(d, { imageModel: '' }); assert.deepEqual(result.jobs, []); assert.equal(d.slides[0].type, 'figure'); assert.equal(d.slides[0].placeholder, true); assert.equal(d.slides[0].image_prompt, 'the admission chest radiograph', 'the prompt is the label'); assert.equal(d.slides[1].type, 'image'); assert.equal(d.slides[1].placeholder, true); assert.equal(d.slides[2].shapes.length, 2, 'the empty image shape is kept, not dropped'); assert.equal(d.slides[2].shapes[1].placeholder, true); assert.equal(d.slides[3].placeholder, undefined); }); test('the frame survives normalisation on both slides and shapes, and only as a boolean', () => { const d = deck(); d.slides[0].placeholder = true; d.slides[2].shapes[1].placeholder = true; const again = deckSchema.normalise(d); assert.equal(again.slides[0].placeholder, true); assert.equal(again.slides[2].shapes[1].placeholder, true); const forged = deckSchema.normalise({ slides: [{ type: 'image', image_prompt: 'x', placeholder: 'yes' }] }); assert.equal(forged.slides[0].placeholder, undefined); }); test('the Word export says what belongs in the frame', () => { const d = deck(); d.slides[0].placeholder = true; d.slides[2].shapes[1].placeholder = true; const blocks = docSpec.fromDeck ? docSpec.fromDeck(d, {}) : docSpec.build(d, {}); const text = JSON.stringify(blocks); assert.match(text, /\[Add an image here: the admission chest radiograph\]/); assert.match(text, /\[Add an image here: a fishbone diagram\]/); assert.doesNotMatch(text, /hand-hygiene/, 'a slide not marked as a frame gets no placeholder line'); }); test('the renderer draws the frame on figure, image and custom image shapes', () => { const py = read('scripts/render_pptx.py'); assert.match(py, /def _placeholder\(slide, left, top, width, height, label\)/); assert.match(py, /MSO_LINE_DASH_STYLE\.DASH/); assert.equal((py.match(/\n\s+_placeholder\(slide, /g) || []).length, 4, 'figure, image, question and draw_image each draw it'); assert.match(py, /"Add an image here"/); }); test('the model is told a picture it names is drawn or left as a frame, never described in prose', () => { const brief = deckSchema.instructions(8, 0); assert.match(brief, /keeps an empty labelled/); assert.match(brief, /Never write an image placeholder into any text/); }); test('a question may carry a picture beside its stem, drawn or left as a frame', async () => { const d = deckSchema.normalise({ slides: [ { type: 'question', heading: 'Case 2', question: 'What does the film show?', options: ['a', 'b'], answer: 'a', image_prompt: 'the admission chest radiograph' } ] }); assert.equal(d.slides[0].type, 'question', 'not converted into a figure slide'); assert.equal(d.slides[0].image_prompt, 'the admission chest radiograph'); await deckBuild.drawFigures(d, { imageModel: '' }); assert.equal(d.slides[0].placeholder, true); const blocks = docSpec.fromDeck(d, {}); assert.match(JSON.stringify(blocks), /\[Add an image here: the admission chest radiograph\]/); const py = read('scripts/render_pptx.py'); assert.match(py, /has_picture = bool\(path and os\.path\.exists\(path\)\) or bool\(spec\.get\("placeholder"\)\)/); });