pediatric-ai-scribe-v3/test/resource-previews.test.js
Daniel cba2a312e1
All checks were successful
Forgejo Docker Build / Root app tests (push) Successful in 51s
Forgejo Docker Build / Build Docker image (push) Successful in 11s
Forgejo Docker Build / End-to-end (browser) (push) Successful in 5s
feat: the preview is a viewer — one page at a time, arrows, keys, swipe, a PDF tab and downloads; the theme leaves the list row
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-13 17:49:22 +02:00

53 lines
3.6 KiB
JavaScript

// A resource, or a theme's sample deck, shown as pages in the page itself —
// on a phone as much as anywhere — rendered once per version.
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 cache key follows the version, so a modification or a re-skin renders afresh', () => {
const previews = require('../src/utils/previewPages');
const a = previews.cacheKey(['resource', 7, '2026-09-13T10:00:00Z', 'ward-teal', 'presentation']);
const same = previews.cacheKey(['resource', 7, '2026-09-13T10:00:00Z', 'ward-teal', 'presentation']);
const edited = previews.cacheKey(['resource', 7, '2026-09-13T11:00:00Z', 'ward-teal', 'presentation']);
const reskinned = previews.cacheKey(['resource', 7, '2026-09-13T10:00:00Z', 'slate', 'presentation']);
assert.equal(a, same);
assert.notEqual(a, edited);
assert.notEqual(a, reskinned);
assert.match(a, /^[0-9a-f]{40}$/, 'a directory name, nothing a caller typed');
});
test('previews are served for a resource and for a theme sample, and only to the owner', () => {
const route = read('src/routes/myResources.js');
assert.match(route, /router\.get\('\/my-resources\/:id\/preview', async/);
assert.match(route, /router\.get\('\/my-resources\/:id\/preview\/:page', async/);
assert.match(route, /router\.get\('\/my-resources\/theme-sample\/:id\/preview', async/);
assert.match(route, /router\.get\('\/my-resources\/theme-sample\/:id\/preview\/:page', async/);
// The resource routes read through the reader rule: the owner, or someone it is shared with.
const preview = route.slice(route.indexOf("router.get('/my-resources/:id/preview'"), route.indexOf("// The sample deck of a theme"));
assert.equal((preview.match(/await readableResource\(req\.params\.id, req\.user\.id/g) || []).length, 2);
// Rendered the way the download is: the same exporter, the same theme.
assert.match(route, /documentExport\.render\(row\.markdown, row\.kind, format,\s*\{ images: figures, deck: row\.deck, theme: row\.theme, figureIds: figureIdList\(row\.image_ids\) \}\)/);
assert.match(route, /previewPages\.cacheKey\(\['resource', row\.id, row\.updated_at, row\.theme \|\| '', row\.kind\]\)/);
assert.match(route, /previewPages\.cacheKey\(\['theme-sample', id, JSON\.stringify\(theme \|\| \{\}\)\]\)/);
});
test('the page offers Preview beside the downloads and beside the theme sample, and the gallery scrolls', () => {
const js = read('public/js/myResources.js');
assert.match(js, /look\.dataset\.preview = String\(row\.id\)/);
assert.match(js, /openPreview\('\/api\/my-resources\/theme-sample\/' \+ encodeURIComponent\(select\.value\) \+ '\/preview'/);
// Fetched with the auth header — an <img src> cannot carry one.
assert.match(js, /fetch\(base \+ '\/' \+ n, \{ headers: getAuthHeaders\(\) \}\)/);
assert.match(js, /URL\.revokeObjectURL\(u\)/, 'blob URLs are released when the preview closes');
// Every presentation takes a theme now, markdown slides included.
assert.doesNotMatch(js, /row\.has_deck !== false && themeCatalogue\.length > 1/);
const css = read('public/css/styles.css');
assert.match(css, /\.mr-preview-page \{[^}]*max-height:100%/);
assert.match(css, /\.mr-preview-pdf \{/);
const mr = read('public/js/myResources.js');
assert.match(mr, /data-view="pdf"/);
assert.match(mr, /e\.key === 'ArrowRight'/);
assert.match(mr, /touchend/);
assert.doesNotMatch(mr, /theme\.dataset\.theme = String\(row\.id\)/, 'the list row no longer carries a theme dropdown');
});