113 lines
5.5 KiB
JavaScript
113 lines
5.5 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
|
|
require('../src/common');
|
|
|
|
const timetableRegionSource = fs.readFileSync(require.resolve('../src/player/regions/timetable.js'), 'utf8');
|
|
|
|
const {
|
|
mediaKind,
|
|
normalizeSlide,
|
|
renderEditorJsContent,
|
|
renderHtmlRegionContent,
|
|
sanitizeRichText
|
|
} = require('../src/player/render-helpers');
|
|
|
|
test('mediaKind classifies player media by extension', () => {
|
|
assert.equal(mediaKind('poster.PNG'), 'image');
|
|
assert.equal(mediaKind('intro.mp4'), 'video');
|
|
assert.equal(mediaKind('manual.pdf'), 'pdf');
|
|
assert.equal(mediaKind('notes.txt'), 'file');
|
|
});
|
|
|
|
test('sanitizeRichText strips unsafe content but preserves allowed markup', () => {
|
|
const html = '<div class="wrap"><a href="https://example.com" target="_blank">Link</a><script>alert(1)</script><span style="color:red">Text</span><table class="grid"><colgroup><col span="1" style="width:50%"><col span="1" style="width:50%"></colgroup><thead><tr><th scope="col">Name</th><th scope="col">Value</th></tr></thead><tbody><tr><td>Alpha</td><td>Beta</td></tr></tbody></table><img src="/media/uploads/photo.png" alt="Photo" loading="lazy" onerror="alert(1)"></div>';
|
|
|
|
assert.equal(
|
|
sanitizeRichText(html),
|
|
'<div class="wrap"><a href="https://example.com" target="_blank" rel="noreferrer noopener">Link</a><span style="color:red">Text</span><table class="grid"><colgroup><col span="1" style="width:50%"><col span="1" style="width:50%"></colgroup><thead><tr><th scope="col">Name</th><th scope="col">Value</th></tr></thead><tbody><tr><td>Alpha</td><td>Beta</td></tr></tbody></table><img src="/media/uploads/photo.png" alt="Photo" loading="lazy"></div>'
|
|
);
|
|
});
|
|
|
|
test('normalizeSlide normalizes nested content without mutating the source', () => {
|
|
const slide = {
|
|
id: 12,
|
|
content: {
|
|
hero: {
|
|
value: '{"headline":"Hello"}',
|
|
font_family: ' Open Sans! ',
|
|
font_size: '42',
|
|
font_color: 'not-a-color',
|
|
type: 'text'
|
|
},
|
|
footer: 'plain text'
|
|
}
|
|
};
|
|
|
|
const normalized = normalizeSlide(slide);
|
|
|
|
assert.notEqual(normalized.content, slide.content);
|
|
assert.deepEqual(normalized.content.hero, {
|
|
value: { headline: 'Hello' },
|
|
font_family: ' Open Sans! ',
|
|
font_size: '42',
|
|
font_color: 'not-a-color',
|
|
type: 'text'
|
|
});
|
|
assert.deepEqual(normalized.content.footer, {
|
|
type: 'text',
|
|
value: 'plain text'
|
|
});
|
|
assert.deepEqual(slide.content.hero, {
|
|
value: '{"headline":"Hello"}',
|
|
font_family: ' Open Sans! ',
|
|
font_size: '42',
|
|
font_color: 'not-a-color',
|
|
type: 'text'
|
|
});
|
|
});
|
|
|
|
test('renderEditorJsContent sanitizes editor blocks and wraps legacy text', () => {
|
|
const editorJson = {
|
|
blocks: [
|
|
{ type: 'header', data: { level: 2, text: '<strong>Title</strong><script>bad()</script>' } },
|
|
{ type: 'paragraph', data: { text: '<a href="javascript:alert(1)">bad</a><em>ok</em>' } },
|
|
{ type: 'list', data: { style: 'ordered', items: ['One', { text: '<span>Two</span>' }] } }
|
|
]
|
|
};
|
|
|
|
assert.equal(
|
|
renderEditorJsContent(editorJson),
|
|
'<h2><strong>Title</strong></h2><p><a>bad</a><em>ok</em></p><ol style="list-style-type:decimal;padding-left:1.4em;"><li>One</li><li><span>Two</span></li></ol>'
|
|
);
|
|
assert.equal(renderEditorJsContent('plain text'), '<p>plain text</p>');
|
|
});
|
|
|
|
test('timetable region registers the timetable type', () => {
|
|
assert.ok(timetableRegionSource.includes("registry.register('timetable'"));
|
|
assert.ok(timetableRegionSource.includes("sanitizeRichText(substituteTimetableVariables(value"));
|
|
});
|
|
|
|
test('shared iframe renderers size preview content explicitly', () => {
|
|
const playerHtmlRegionSource = fs.readFileSync(require.resolve('../src/player/regions/html.js'), 'utf8');
|
|
const playerWebpageRegionSource = fs.readFileSync(require.resolve('../src/player/regions/webpage.js'), 'utf8');
|
|
const playerRenderHelpersSource = fs.readFileSync(require.resolve('../src/player/render-helpers.js'), 'utf8');
|
|
const slideThumbnailPreviewSource = fs.readFileSync(require.resolve('../src/web/lib/media/slide-thumbnail-preview.js'), 'utf8');
|
|
const slideThumbnailsSource = fs.readFileSync(require.resolve('../src/web/lib/media/slide-thumbnails.js'), 'utf8');
|
|
|
|
assert.ok(playerHtmlRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
|
assert.ok(playerWebpageRegionSource.includes('style="width:100%;height:100%;border:0;display:block;background:#fff;overflow:hidden;"'));
|
|
assert.ok(playerRenderHelpersSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
|
assert.ok(slideThumbnailPreviewSource.includes('slide-preview-webpage-region'));
|
|
assert.ok(slideThumbnailPreviewSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
|
assert.ok(slideThumbnailsSource.includes('slide-preview-webpage-placeholder'));
|
|
assert.ok(slideThumbnailsSource.includes('style="width:100%;height:100%;border:0;display:block;background:transparent;overflow:hidden;"'));
|
|
});
|
|
|
|
test('html region helpers unwrap object-shaped values before rendering', () => {
|
|
const rendered = renderHtmlRegionContent({ html: '<div>Hi</div>' });
|
|
|
|
assert.match(rendered, /srcdoc="<!doctype html><html><head><style>html,body\{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;\}<\/style><\/head><body><div>Hi<\/div><\/body><\/html>"/);
|
|
assert.doesNotMatch(rendered, /\[object Object\]/);
|
|
}); |