A PowerPoint or Word file cannot be shown in a browser and a phone has nowhere to open one. Preview renders the resource the way its download would be built, turns it into one PNG per page (Gotenberg to PDF, pdftoppm to pages), and shows the pages in an overlay that scrolls and pinch-zooms like anything else. Rendered once per version — updated_at and theme are in the key — and served from disk afterwards. The theme picker's sample deck has the same Preview beside its download. Every presentation now shows the theme picker in the library, since markdown slides take a theme too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
48 lines
3.3 KiB
JavaScript
48 lines
3.3 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 with the owner in the WHERE, like every other route here.
|
|
const preview = route.slice(route.indexOf("router.get('/my-resources/:id/preview'"), route.indexOf("// The sample deck of a theme"));
|
|
assert.equal((preview.match(/AND 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-pages \{[^}]*overflow:auto/);
|
|
assert.match(css, /\.mr-preview-page \{[^}]*width:100%/);
|
|
});
|