Release 2.19.3
This commit is contained in:
1 parent
55ee5dc366
commit
2e603caeb6
19 files changed
+351
-28
No files matched your search
@@ -170,7 +170,7 @@ test('API editor preview resolves zero-based indexed placeholders from sorted re
|
||||
assert.doesNotMatch(preview, /Cam/);
|
||||
});
|
||||
|
||||
test('API image bounds guide is limited to the small preview and hidden in the popup', () => {
|
||||
test('image and icon placeholder bounds are hidden in the popup but remain in the editor', () => {
|
||||
const editor = loadApiEditor();
|
||||
const preview = editor.renderPreview({ id: 23, region_key: 'apiRegion', fontFamily: 'Arial', fontSize: 20, fontColor: '#000000' }, {
|
||||
source_id: 11,
|
||||
@@ -181,10 +181,16 @@ test('API image bounds guide is limited to the small preview and hidden in the p
|
||||
existingContent: {}
|
||||
});
|
||||
const popupStyles = fs.readFileSync(path.join(__dirname, '../src/web/views/signage/slides/popup-preview.hbs'), 'utf8');
|
||||
const rssSource = fs.readFileSync(path.join(__dirname, '../src/web/public/js/regions/type/rss.js'), 'utf8');
|
||||
const weatherSource = fs.readFileSync(path.join(__dirname, '../src/web/public/js/regions/type/weather.js'), 'utf8');
|
||||
const weatherIconsSource = fs.readFileSync(path.join(__dirname, '../src/web/public/js/shared/weather-icons.js'), 'utf8');
|
||||
|
||||
assert.match(preview, /class="api-image-placeholder-bounds"/);
|
||||
assert.match(preview, /class="api-image-placeholder-bounds slide-preview-image-placeholder-bounds"/);
|
||||
assert.match(preview, /border:1px dashed/);
|
||||
assert.match(popupStyles, /#popup-preview-canvas \.api-image-placeholder-bounds\s*\{\s*border:\s*0 !important;/);
|
||||
assert.match(rssSource, /class="slide-preview-image-placeholder-bounds"/);
|
||||
assert.match(weatherSource, /class="slide-preview-image-placeholder-bounds"/);
|
||||
assert.match(weatherIconsSource, /class="slide-preview-image-placeholder-bounds"/);
|
||||
assert.match(popupStyles, /#popup-preview-canvas \.slide-preview-image-placeholder-bounds,[\s\S]*?#popup-preview-canvas \.api-image-placeholder-bounds\s*\{\s*border:\s*0 !important;[\s\S]*?outline:\s*0 !important;[\s\S]*?box-shadow:\s*none !important;/);
|
||||
});
|
||||
|
||||
test('API region plain placeholders work with a single object response', () => {
|
||||
|
||||
@@ -97,8 +97,15 @@ test('slide thumbnail previews treat image-only text as visible content', () =>
|
||||
|
||||
test('popup preview falls back to iframe sizing rules', () => {
|
||||
const popupPreviewSource = fs.readFileSync(require.resolve('../src/web/views/signage/slides/popup-preview.hbs'), 'utf8');
|
||||
const stylesheet = fs.readFileSync(require.resolve('../src/web/public/css/theme-custom.css'), 'utf8');
|
||||
|
||||
assert.ok(popupPreviewSource.includes('/assets/js/regions/chart-data.js'));
|
||||
assert.ok(popupPreviewSource.includes('slideEditorScripts'));
|
||||
assert.ok(popupPreviewSource.includes('/assets/js/shared/slide-preview-renderer.js'));
|
||||
assert.ok(popupPreviewSource.includes('window.pulseSlidePreviewRenderer.render(payload)'));
|
||||
assert.ok(popupPreviewSource.includes('background: var(--bs-body-bg);'));
|
||||
assert.ok(popupPreviewSource.includes('color: var(--bs-body-color);'));
|
||||
assert.match(stylesheet, /\.slide-preview-popup-body\s*\{[^}]*background:\s*var\(--bs-body-bg\);[^}]*color:\s*var\(--bs-body-color\);/s);
|
||||
assert.match(stylesheet, /\.slide-preview-popup-stage\s*\{[^}]*background:\s*var\(--bs-body-bg\);/s);
|
||||
assert.ok(popupPreviewSource.includes('.slide-preview-chart-region canvas'));
|
||||
assert.ok(popupPreviewSource.includes('background: transparent !important'));
|
||||
assert.ok(popupPreviewSource.includes('#popup-preview-canvas iframe'));
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const { render } = require('../src/web/public/js/shared/slide-preview-renderer');
|
||||
const { buildThumbnailPreviewPayload } = require('../src/web/lib/media/slide-thumbnails');
|
||||
const placeholderUtils = require('../src/web/public/js/shared/placeholder-utils');
|
||||
|
||||
function loadEditorTimeDateModule() {
|
||||
const source = fs.readFileSync(require.resolve('../src/web/public/js/regions/type/time-date.js'), 'utf8');
|
||||
const modules = new Map();
|
||||
const sandbox = {
|
||||
window: {
|
||||
pulseRegionTypes: {
|
||||
register(type, definition) {
|
||||
modules.set(type, definition);
|
||||
}
|
||||
},
|
||||
pulseRegionUtils: {
|
||||
escapeHtml(value) {
|
||||
return String(value).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||
},
|
||||
sanitizePreviewHtml(value) {
|
||||
return String(value);
|
||||
}
|
||||
},
|
||||
placeholderUtils: placeholderUtils
|
||||
}
|
||||
};
|
||||
|
||||
vm.runInNewContext(source, sandbox, { filename: 'time-date.js' });
|
||||
return modules.get('time-date');
|
||||
}
|
||||
|
||||
test('thumbnail payload passes saved region geometry and content to the popup renderer', () => {
|
||||
const payload = buildThumbnailPreviewPayload({
|
||||
template: {
|
||||
canvas_size_width: 1280,
|
||||
canvas_size_height: 720,
|
||||
regions: [{ id: 14, region_key: 'clock', region_type: 'time-date', x: 20, y: 30, width: 400, height: 90, z_index: 3 }]
|
||||
},
|
||||
content: { clock: { value: '{{hh}}:{{mm}}', timezone: 'Europe/London' } }
|
||||
});
|
||||
|
||||
assert.equal(payload.regions[0].x, 20);
|
||||
assert.equal(payload.regions[0].width, 400);
|
||||
assert.equal(payload.regions[0].content.value, '{{hh}}:{{mm}}');
|
||||
assert.equal(payload.existingContent.clock.timezone, 'Europe/London');
|
||||
});
|
||||
|
||||
test('thumbnail regions use shared region renderers and their source context', () => {
|
||||
const calls = [];
|
||||
const registry = {
|
||||
get(type) {
|
||||
return {
|
||||
renderPreview(...args) {
|
||||
calls.push({ type, args });
|
||||
return '<span>' + type + '</span>';
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
const apiSources = [{ id: 8, responseJson: [{ name: 'Ada' }] }];
|
||||
const payload = {
|
||||
regions: [
|
||||
{ id: 1, region_key: 'clock', region_type: 'time-date', x: 20, y: 12, width: 200, height: 80, z_index: 4, content: { value: '{{hh}}:{{mm}}', timezone: 'UTC' } },
|
||||
{ id: 2, region_key: 'donations', region_type: 'api', x: 220, y: 12, width: 300, height: 80, z_index: 2, content: { source_id: 8, value: '{{name}}' } }
|
||||
],
|
||||
apiSources: apiSources,
|
||||
rssFeeds: [],
|
||||
weatherLocations: [],
|
||||
timetableGroups: []
|
||||
};
|
||||
|
||||
const html = render(payload, registry);
|
||||
|
||||
assert.match(html, /data-region-key="clock"[^>]*><span>time-date<\/span>/);
|
||||
assert.match(html, /data-region-key="donations"[^>]*><span>api<\/span>/);
|
||||
assert.equal(calls[0].args[3], 1);
|
||||
assert.equal(calls[1].args[2].sources, apiSources);
|
||||
});
|
||||
|
||||
test('thumbnail time/date uses the editor renderer to expand current-time tokens', () => {
|
||||
const registry = {
|
||||
get(type) {
|
||||
return type === 'time-date' ? loadEditorTimeDateModule() : null;
|
||||
}
|
||||
};
|
||||
const html = render({
|
||||
regions: [{
|
||||
id: 4,
|
||||
region_key: 'clock',
|
||||
region_type: 'time-date',
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 320,
|
||||
height: 80,
|
||||
content: { value: '{{hh}}:{{mm}}', timezone: 'UTC' }
|
||||
}]
|
||||
}, registry);
|
||||
|
||||
assert.match(html, /<div class="template-region time-date"/);
|
||||
assert.match(html, /class="template-region-text-scale"[^>]*>\d{2}:\d{2}<\/div>/);
|
||||
});
|
||||
@@ -19,6 +19,26 @@ test('slide table thumbnails size to the canvas ratio and show the whole image',
|
||||
assert.match(placeholderRule, /min-height:\s*0;/);
|
||||
});
|
||||
|
||||
test('slide list thumbnails open and release an authorized preview modal', () => {
|
||||
const listTemplate = fs.readFileSync(path.join(__dirname, '..', 'src', 'web', 'views', 'signage', 'slides', 'list.hbs'), 'utf8');
|
||||
const slidesRoutes = fs.readFileSync(path.join(__dirname, '..', 'src', 'web', 'routes', 'signage', 'slides', 'routes.js'), 'utf8');
|
||||
const stylesheet = fs.readFileSync(path.join(__dirname, '..', 'src', 'web', 'public', 'css', 'theme-custom.css'), 'utf8');
|
||||
|
||||
assert.match(listTemplate, /class="playlist-slide-thumb slide-list-thumbnail-button"[^>]*data-bs-toggle="modal"/);
|
||||
assert.match(listTemplate, /data-slide-preview-url="\/api\/internal\/slide-thumbnails\/\{\{id\}\}\/popup-preview"/);
|
||||
assert.match(listTemplate, /data-canvas-width="\{\{canvas_width\}\}" data-canvas-height="\{\{canvas_height\}\}"/);
|
||||
assert.match(listTemplate, /currentAspectRatio = canvasWidth > 0 && canvasHeight > 0 \? canvasWidth \/ canvasHeight : 16 \/ 9/);
|
||||
assert.match(listTemplate, /aria-label="Preview slide: \{\{title\}\}"/);
|
||||
assert.match(listTemplate, /data-slide-preview-frame/);
|
||||
assert.match(listTemplate, /frame\.src = 'about:blank'/);
|
||||
assert.match(slidesRoutes, /\/api\/internal\/slide-thumbnails\/:id\/popup-preview', requirePopupPreviewAccess, async function/);
|
||||
assert.match(stylesheet, /^\.slide-list-thumbnail-button:focus-visible\s*\{/m);
|
||||
const previewFrameRule = stylesheet.match(/^\.slide-list-preview-frame\s*\{([^}]+)\}/m)[1];
|
||||
assert.match(previewFrameRule, /background:\s*var\(--bs-body-bg\);/);
|
||||
assert.match(previewFrameRule, /aspect-ratio:\s*var\(--slide-preview-aspect-ratio, 16 \/ 9\);/);
|
||||
assert.match(stylesheet, /#slide-preview-modal \.modal-content\s*\{\s*overflow:\s*hidden;/);
|
||||
});
|
||||
|
||||
test('standalone slide thumbnail columns shrink to their content and keep thumbnails centered', () => {
|
||||
const stylesheet = fs.readFileSync(path.join(__dirname, '..', 'src', 'web', 'public', 'css', 'theme-custom.css'), 'utf8');
|
||||
const columnRule = stylesheet.match(/^td\[data-label="Thumbnail"\] > \.playlist-slide-thumb\s*\{([^}]+)\}/m)[1];
|
||||
|
||||
Reference in new issue
Block a user