Release v2.14.1
This commit is contained in:
1 parent
635e1a9942
commit
c3299abaac
174 files changed
+4483
-1687
No files matched your search
@@ -2,6 +2,9 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const packageVersion = require('../package.json').version;
|
||||
const renderingShared = require('../src/player/rendering-shared');
|
||||
const { renderPlayerPage, renderPlayerOnboardingFormPage } = require('../src/player/render');
|
||||
|
||||
require('../src/common');
|
||||
|
||||
@@ -13,7 +16,8 @@ const {
|
||||
renderEditorJsContent,
|
||||
renderHtmlRegionContent,
|
||||
sanitizeRichText,
|
||||
getAnnouncementIconsDataScript
|
||||
getAnnouncementIconsDataScript,
|
||||
getPlayerRuntimeScripts
|
||||
} = require('../src/player/render-helpers');
|
||||
|
||||
test('mediaKind classifies player media by extension', () => {
|
||||
@@ -52,9 +56,9 @@ test('normalizeSlide normalizes nested content without mutating the source', ()
|
||||
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',
|
||||
font_family: 'Open Sans',
|
||||
font_size: 42,
|
||||
font_color: '#000000',
|
||||
type: 'text'
|
||||
});
|
||||
assert.deepEqual(normalized.content.footer, {
|
||||
@@ -70,6 +74,27 @@ test('normalizeSlide normalizes nested content without mutating the source', ()
|
||||
});
|
||||
});
|
||||
|
||||
test('server and browser slide normalization stay in sync', () => {
|
||||
const browserContext = { window: {} };
|
||||
const sharedRenderingSource = fs.readFileSync(require.resolve('../src/player/rendering-shared.js'), 'utf8');
|
||||
const browserRenderingSource = fs.readFileSync(require.resolve('../src/player/public/js/player-page-rendering.js'), 'utf8');
|
||||
const slide = {
|
||||
content: {
|
||||
hero: {
|
||||
value: '{"headline":"Hello"}',
|
||||
font_family: ' Open Sans! ',
|
||||
font_size: '42',
|
||||
font_color: 'not-a-color',
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
};
|
||||
vm.runInNewContext(sharedRenderingSource, browserContext);
|
||||
vm.runInNewContext(browserRenderingSource, browserContext);
|
||||
|
||||
assert.equal(JSON.stringify(browserContext.normalizeSlide(slide)), JSON.stringify(normalizeSlide(slide)));
|
||||
});
|
||||
|
||||
test('renderEditorJsContent sanitizes editor blocks and wraps legacy text', () => {
|
||||
const editorJson = {
|
||||
blocks: [
|
||||
@@ -86,6 +111,90 @@ test('renderEditorJsContent sanitizes editor blocks and wraps legacy text', () =
|
||||
assert.equal(renderEditorJsContent('plain text'), '<p>plain text</p>');
|
||||
});
|
||||
|
||||
test('region animation decoration preserves plain markup and annotates animated roots', () => {
|
||||
const plainMarkup = '<div class="template-region">Text</div>';
|
||||
const animationConfig = { intro: { preset: 'fadeIn', duration_ms: 700 } };
|
||||
|
||||
assert.equal(renderingShared.decorateRegionMarkup(plainMarkup, { animationConfig: {} }), plainMarkup);
|
||||
assert.equal(
|
||||
renderingShared.decorateRegionMarkup(plainMarkup, { animationConfig: animationConfig }),
|
||||
'<div class="template-region" data-animation-json="{"intro":{"preset":"fadeIn","duration_ms":700}}">Text</div>'
|
||||
);
|
||||
|
||||
const browserContext = { window: {} };
|
||||
vm.runInNewContext(fs.readFileSync(require.resolve('../src/player/rendering-shared.js'), 'utf8'), browserContext);
|
||||
vm.runInNewContext(fs.readFileSync(require.resolve('../src/player/public/js/player-page-rendering.js'), 'utf8'), browserContext);
|
||||
browserContext.getTemplateRenderPlan = function () {
|
||||
return {
|
||||
layout: {
|
||||
regions: [{ regionKey: 'hero', regionType: 'text', animationJson: JSON.stringify(animationConfig) }],
|
||||
canvasWidth: 1920,
|
||||
canvasHeight: 1080,
|
||||
backgroundColor: '#000000',
|
||||
backgroundGradient: '',
|
||||
background: ''
|
||||
},
|
||||
renderRegion: function () { return plainMarkup; }
|
||||
};
|
||||
};
|
||||
browserContext.normalizePlayerAnimationConfig = function (value) { return JSON.parse(value); };
|
||||
browserContext.selectPlayerMediaSource = function () { return ''; };
|
||||
browserContext.setPlayerCanvasDimensions = function () {};
|
||||
browserContext.buildBackdropStyle = function () { return ''; };
|
||||
browserContext.renderSlideShell = function (_slide, _canvasClass, _width, _height, content) { return content; };
|
||||
|
||||
const renderedSlide = browserContext.renderTemplateSlideMarkup({ template: {}, content: {} });
|
||||
assert.match(renderedSlide, /data-animation-json=/);
|
||||
});
|
||||
|
||||
test('browser rendering helpers select media and expose playlist revision and canvas helpers', () => {
|
||||
const styles = {};
|
||||
const browserContext = {
|
||||
window: {},
|
||||
document: {
|
||||
documentElement: {
|
||||
style: {
|
||||
setProperty: function (key, value) { styles[key] = value; }
|
||||
}
|
||||
}
|
||||
},
|
||||
currentPlaylistEtag: '"cached-revision"'
|
||||
};
|
||||
vm.runInNewContext(fs.readFileSync(require.resolve('../src/player/rendering-shared.js'), 'utf8'), browserContext);
|
||||
vm.runInNewContext(fs.readFileSync(require.resolve('../src/player/public/js/player-page-rendering.js'), 'utf8'), browserContext);
|
||||
|
||||
assert.equal(browserContext.selectPlayerMediaSource('/media/original.jpg', [
|
||||
{ url: '/media/small.jpg', width: 400 },
|
||||
{ url: '/media/large.jpg', width: 1200 }
|
||||
], 800), '/media/large.jpg');
|
||||
assert.equal(browserContext.getPlaylistRevision({ revision: 'new-revision' }), 'new-revision');
|
||||
assert.equal(browserContext.getPlaylistRevision({}), 'cached-revision');
|
||||
browserContext.setPlayerCanvasDimensions(1920, 1080);
|
||||
assert.deepEqual(styles, {
|
||||
'--player-canvas-width': '1920px',
|
||||
'--player-canvas-height': '1080px'
|
||||
});
|
||||
});
|
||||
|
||||
test('server and browser rich-text rendering stay in sync', () => {
|
||||
const browserContext = {
|
||||
window: {}
|
||||
};
|
||||
const sharedRenderingSource = fs.readFileSync(require.resolve('../src/player/rendering-shared.js'), 'utf8');
|
||||
const browserRenderingSource = fs.readFileSync(require.resolve('../src/player/public/js/player-page-rendering.js'), 'utf8');
|
||||
vm.runInNewContext(sharedRenderingSource, browserContext);
|
||||
vm.runInNewContext(browserRenderingSource, browserContext);
|
||||
|
||||
const value = {
|
||||
blocks: [{
|
||||
type: 'paragraph',
|
||||
data: { text: '<table><tbody class="body"><tr><td>Cell</td></tr></tbody></table>' }
|
||||
}]
|
||||
};
|
||||
|
||||
assert.equal(browserContext.renderEditorJsContent(value), renderEditorJsContent(value));
|
||||
});
|
||||
|
||||
test('shared API progress placeholder renders a clamped bar from numeric fields', () => {
|
||||
const sandbox = { window: {} };
|
||||
vm.runInNewContext(fs.readFileSync(require.resolve('../src/web/public/js/shared/placeholder-utils.js'), 'utf8'), sandbox);
|
||||
@@ -170,6 +279,34 @@ test('player announcement bootstrap includes catalog-only icons', () => {
|
||||
assert.match(getAnnouncementIconsDataScript(), /bank/);
|
||||
});
|
||||
|
||||
test('onboarding form page injects its configured device id', () => {
|
||||
const page = renderPlayerOnboardingFormPage('device-123');
|
||||
|
||||
assert.match(page, /var deviceId = "device-123";/);
|
||||
});
|
||||
|
||||
test('player runtime script URLs include content fingerprints', () => {
|
||||
const page = renderPlayerPage('test', { slides: [] });
|
||||
|
||||
assert.match(page, new RegExp('player-script/playback\\.js\\?v=' + packageVersion.replace(/\./g, '\\.') + '-[a-f0-9]{12}'));
|
||||
});
|
||||
|
||||
test('player runtime script assembly includes shared and region scripts once', () => {
|
||||
const runtimeScripts = getPlayerRuntimeScripts();
|
||||
const scriptNames = runtimeScripts.map(function (entry) { return entry[0]; });
|
||||
const sharedRenderingIndex = scriptNames.indexOf('rendering-shared');
|
||||
const pageRenderingIndex = scriptNames.indexOf('rendering');
|
||||
const htmlRegionScript = runtimeScripts.find(function (entry) {
|
||||
return entry[0].startsWith('region-') && entry[1].includes("registry.register('html'");
|
||||
});
|
||||
|
||||
assert.ok(sharedRenderingIndex >= 0 && sharedRenderingIndex < pageRenderingIndex);
|
||||
assert.ok(scriptNames.includes('placeholder-utils'));
|
||||
assert.ok(scriptNames.includes('qr-code-svg'));
|
||||
assert.ok(htmlRegionScript);
|
||||
assert.equal(new Set(scriptNames).size, scriptNames.length);
|
||||
});
|
||||
|
||||
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');
|
||||
@@ -191,4 +328,66 @@ test('html region helpers unwrap object-shaped values before rendering', () => {
|
||||
|
||||
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\]/);
|
||||
});
|
||||
|
||||
test('shared render value normalization follows caller key order recursively', () => {
|
||||
assert.equal(
|
||||
renderingShared.normalizeRenderableString({ value: { content: '<strong>Hi</strong>' } }, ['value', 'text', 'html', 'content']),
|
||||
'<strong>Hi</strong>'
|
||||
);
|
||||
assert.equal(
|
||||
renderingShared.normalizeRenderableString({ value: { url: 'https://example.test' } }, ['value', 'text', 'url', 'href', 'src', 'content']),
|
||||
'https://example.test'
|
||||
);
|
||||
assert.equal(renderingShared.normalizeRenderableString(null, ['value', 'text']), '');
|
||||
});
|
||||
|
||||
test('shared region value normalization preserves selected values and stringifies other objects', () => {
|
||||
const nestedValue = { amount: 12 };
|
||||
assert.equal(renderingShared.normalizeRegionValue({ value: nestedValue }, ['value', 'text', 'html']), nestedValue);
|
||||
assert.equal(renderingShared.normalizeRegionValue({ text: 'hello' }, ['value', 'text', 'html']), 'hello');
|
||||
assert.equal(renderingShared.normalizeRegionValue({ amount: 12 }, ['value', 'text', 'html']), '{"amount":12}');
|
||||
assert.equal(renderingShared.normalizeRegionValue(12, ['value', 'text', 'html']), 12);
|
||||
const circularValue = {};
|
||||
circularValue.self = circularValue;
|
||||
assert.equal(renderingShared.normalizeRegionValue(circularValue, ['value', 'text', 'html']), '');
|
||||
});
|
||||
|
||||
test('shared HTML document builder wraps fragments and preserves complete documents', () => {
|
||||
assert.equal(
|
||||
renderingShared.buildHtmlDocument('<main>Hi</main>'),
|
||||
'<!doctype html><html><head><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:transparent;}</style></head><body><main>Hi</main></body></html>'
|
||||
);
|
||||
assert.equal(renderingShared.buildHtmlDocument('<!doctype html><html><body>Hi</body></html>'), '<!doctype html><html><body>Hi</body></html>');
|
||||
assert.equal(renderingShared.buildHtmlDocument(' '), '');
|
||||
});
|
||||
|
||||
test('browser HTML and webpage regions preserve their distinct output modes', () => {
|
||||
const modules = {};
|
||||
const browserContext = {
|
||||
window: {
|
||||
pulsePlayerRegionTypes: {
|
||||
register: function (type, regionModule) {
|
||||
modules[type] = regionModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
[
|
||||
'../src/player/rendering-shared.js',
|
||||
'../src/player/public/js/player-page-rendering.js',
|
||||
'../src/player/regions/html.js',
|
||||
'../src/player/regions/webpage.js'
|
||||
].forEach(function (scriptPath) {
|
||||
vm.runInNewContext(fs.readFileSync(require.resolve(scriptPath), 'utf8'), browserContext);
|
||||
});
|
||||
|
||||
const htmlFragment = modules.html.renderRegion({ baseStyle: '' }, { value: { content: '<strong>Hi</strong>' } });
|
||||
const htmlDocument = modules.html.renderRegion({ baseStyle: '' }, { value: '<!doctype html><html><body>Hi</body></html>' });
|
||||
const webpage = modules.webpage.renderRegion({ baseStyle: '', label: 'Weather' }, { value: { url: 'https://example.test' } });
|
||||
|
||||
assert.match(htmlFragment, /template-region-html-content/);
|
||||
assert.match(htmlFragment, /<strong>Hi<\/strong>/);
|
||||
assert.match(htmlDocument, /sandbox=""/);
|
||||
assert.match(webpage, /src="https:\/\/example\.test"/);
|
||||
});
|
||||
Reference in new issue
Block a user