const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const slideFormEditorSource = fs.readFileSync(require.resolve('../src/web/public/js/slides/slide-form-editor.js'), 'utf8'); const slideFormSource = fs.readFileSync(require.resolve('../src/web/public/js/slides/slide-form.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'); const slideHtmlRegionSource = fs.readFileSync(require.resolve('../src/web/public/js/regions/type/html.js'), 'utf8'); const slideWebpageRegionSource = fs.readFileSync(require.resolve('../src/web/public/js/regions/type/webpage.js'), 'utf8'); test('slide editor disables pasted data images in TinyMCE', () => { assert.ok(slideFormEditorSource.includes('paste_data_images: false')); assert.ok(slideFormEditorSource.includes('paste_block_drop: true')); assert.ok(slideFormEditorSource.includes('resize: false')); }); test('slide editor enables server-backed image uploads', () => { assert.ok(slideFormEditorSource.includes("plugins: 'lists code advlist fullscreen table image'")); assert.ok(slideFormEditorSource.includes('automatic_uploads: true')); assert.ok(slideFormEditorSource.includes('images_file_types: imageUploadFileTypes')); assert.ok(slideFormEditorSource.includes('images_upload_handler: uploadEditorImage')); assert.ok(slideFormEditorSource.includes('table image chip | fullscreen')); assert.ok(slideFormEditorSource.includes('relative_urls: false')); assert.ok(slideFormEditorSource.includes('remove_script_host: false')); }); test('slide editor keeps normal TinyMCE image insertion unchanged', () => { assert.doesNotMatch(slideFormEditorSource, /bindPlaceholderImageSupport|placeholderFallbackSource/); assert.ok(slideFormEditorSource.includes('images_upload_handler: uploadEditorImage')); }); test('slide editor inserts tables with zero padding and spacing by default', () => { assert.ok(slideFormEditorSource.includes("table_default_attributes: {")); assert.ok(slideFormEditorSource.includes("cellpadding: '0'")); assert.ok(slideFormEditorSource.includes("cellspacing: '0'")); assert.ok(slideFormEditorSource.includes('td, th { border: 1px solid currentColor; padding: 0; vertical-align: top; }')); }); test('slide editor uses a smaller wysiwyg image limit', () => { assert.ok(slideFormEditorSource.includes('var imageUploadMaxBytes = Math.max(1, Number(settings.imageUploadMaxBytes || 2 * 1024 * 1024));')); assert.ok(slideFormEditorSource.includes('Image must be ')); assert.ok(slideFormEditorSource.includes('Larger images should use the dedicated Image region.')); assert.ok(slideFormEditorSource.includes("xhr.setRequestHeader('X-Upload-Context', imageUploadContext);")); }); test('slide editor tracks uploaded image paths for cleanup', () => { assert.ok(slideFormEditorSource.includes('var editorImageUploadPaths = new Set();')); assert.ok(slideFormEditorSource.includes('getImageUploadCleanupPaths')); assert.ok(slideFormEditorSource.includes('getCommittedImageUploadCleanupPaths')); assert.ok(slideFormEditorSource.includes('getPendingImageUploadPaths')); assert.ok(slideFormEditorSource.includes('clearImageUploadPaths')); }); test('slide editor marks the owning form dirty after TinyMCE edits', () => { assert.ok(slideFormEditorSource.includes("var markFormDirty = typeof settings.markFormDirty === 'function' ? settings.markFormDirty : function () {};")); assert.match(slideFormEditorSource, /if \(hydrated\)\s*\{\s*markFormDirty\(\);/); assert.match(slideFormSource, /markFormDirty:\s*function \(\)\s*\{\s*slideForm\.dataset\.dirty = 'true';/); }); test('slide editor keeps image-only rich text from being treated as empty', () => { assert.ok(slideFormEditorSource.includes('/ { assert.ok(slideFormSource.includes('getImageUploadCleanupPaths')); assert.ok(slideFormSource.includes('getCommittedImageUploadCleanupPaths')); assert.ok(slideFormSource.includes("regionMediaController.queueUploadCleanup(slideFormEditorController.getImageUploadCleanupPaths())")); }); test('slide thumbnail previews treat image-only text as visible content', () => { assert.ok(slideThumbnailPreviewSource.includes('/ { const popupPreviewSource = fs.readFileSync(require.resolve('../src/web/views/signage/slides/popup-preview.hbs'), 'utf8'); assert.ok(popupPreviewSource.includes('#popup-preview-canvas iframe')); assert.ok(popupPreviewSource.includes('#popup-preview-canvas .slide-preview-webpage-region iframe')); assert.ok(popupPreviewSource.includes('#popup-preview-canvas .slide-preview-html-region iframe')); }); test('browser preview helpers normalize object-shaped HTML and webpage values', () => { assert.ok(slideHtmlRegionSource.includes('normalizePreviewValue')); assert.ok(slideWebpageRegionSource.includes('normalizePreviewValue')); }); test('webpage preview updates only through the explicit button', () => { assert.ok(slideWebpageRegionSource.includes('input-group flex-nowrap')); assert.ok(slideWebpageRegionSource.includes('data-webpage-preview-update')); assert.ok(slideFormSource.includes('input[type="url"][name^="region_webpage_"]')); assert.ok(slideFormSource.includes('requestPreviewRender();')); });