const fs = require('node:fs'); const path = require('node:path'); const vm = require('node:vm'); const test = require('node:test'); const assert = require('node:assert/strict'); test('slide table thumbnails size to the canvas ratio and show the whole image', () => { const stylesheet = fs.readFileSync(path.join(__dirname, '..', 'src', 'web', 'public', 'css', 'theme-custom.css'), 'utf8'); const thumbRule = stylesheet.match(/^\.playlist-slide-thumb\s*\{([^}]+)\}/m)[1]; const imageRule = stylesheet.match(/^\.playlist-slide-thumb-image\s*\{([^}]+)\}/m)[1]; const placeholderRule = stylesheet.match(/^\.playlist-slide-thumb-placeholder\s*\{([^}]+)\}/m)[1]; assert.match(thumbRule, /width:\s*min\(5\.5rem, calc\(5\.5rem \* \(var\(--playlist-slide-thumb-aspect-ratio, 16 \/ 9\)\)\)\);/); assert.match(thumbRule, /aspect-ratio:\s*var\(--playlist-slide-thumb-aspect-ratio, 16 \/ 9\);/); assert.match(thumbRule, /min-height:\s*0;/); assert.match(thumbRule, /flex:\s*0 0 auto;/); assert.match(imageRule, /object-fit:\s*contain;/); assert.match(placeholderRule, /min-width:\s*0;/); assert.match(placeholderRule, /min-height:\s*0;/); }); test('standalone slide thumbnails are centered within their table column', () => { 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]; const cellRule = stylesheet.match(/^\.card \.table td\[data-label="Thumbnail"\]\s*\{([^}]+)\}/m)[1]; assert.match(columnRule, /margin-inline:\s*auto;/); assert.match(cellRule, /padding-inline:\s*1\.5rem;/); }); test('mobile responsive tables keep descriptive columns wide enough for phrases', () => { const stylesheet = fs.readFileSync(path.join(__dirname, '..', 'src', 'web', 'public', 'css', 'theme-custom.css'), 'utf8'); const mobileRule = stylesheet.match(/@media \(max-width: 767\.98px\)\s*\{\s*\.table-responsive > table\.table > tbody > tr > td:is\(([\s\S]*?)\)\s*\{([^}]+)\}/); assert.ok(mobileRule); for (const label of ['Name', 'Title', 'Template', 'Description', 'Client', 'Current Screen', 'Current Slide', 'Slides']) { assert.ok(mobileRule[1].includes('[data-label="' + label + '"]')); } assert.match(mobileRule[2], /min-width:\s*14rem;/); assert.match(mobileRule[2], /overflow-wrap:\s*break-word;/); }); test('table pagination cards skip the minimum height for short content', () => { const cardStyle = { properties: {}, setProperty(name, value) { this.properties[name] = String(value); }, removeProperty(name) { delete this.properties[name]; } }; const card = { style: cardStyle, scrollHeight: 200, getBoundingClientRect() { return { top: 100 }; } }; const bodyClassList = { toggled: null, toggle(name, value) { this.toggled = { name, value }; } }; const sandbox = { document: { body: { classList: bodyClassList }, querySelector(selector) { return selector === '[data-table-pagination-card]' ? card : null; }, querySelectorAll(selector) { return selector === '[data-table-pagination-card]' ? [card] : []; } }, window: null, URL, console, setTimeout, clearTimeout }; sandbox.window = Object.assign(sandbox, { innerHeight: 280, addEventListener() {}, clearTimeout, setTimeout, requestAnimationFrame(callback) { callback(); } }); const scriptPath = path.join(__dirname, '..', 'src', 'web', 'public', 'js', 'table', 'table-search.js'); const script = fs.readFileSync(scriptPath, 'utf8'); vm.runInNewContext(script, sandbox, { filename: scriptPath }); assert.equal(cardStyle.properties['--table-pagination-card-min-height'], undefined); assert.equal(cardStyle.properties['--table-pagination-card-max-height'], '320px'); assert.deepEqual(bodyClassList.toggled, { name: 'table-pagination-page', value: true }); }); test('table pagination cards keep the minimum height for tall content', () => { const cardStyle = { properties: {}, setProperty(name, value) { this.properties[name] = String(value); }, removeProperty(name) { delete this.properties[name]; } }; const card = { style: cardStyle, scrollHeight: 640, getBoundingClientRect() { return { top: 100 }; } }; const bodyClassList = { toggled: null, toggle(name, value) { this.toggled = { name, value }; } }; const sandbox = { document: { body: { classList: bodyClassList }, querySelector(selector) { return selector === '[data-table-pagination-card]' ? card : null; }, querySelectorAll(selector) { return selector === '[data-table-pagination-card]' ? [card] : []; } }, window: null, URL, console, setTimeout, clearTimeout }; sandbox.window = Object.assign(sandbox, { innerHeight: 280, addEventListener() {}, clearTimeout, setTimeout, requestAnimationFrame(callback) { callback(); } }); const scriptPath = path.join(__dirname, '..', 'src', 'web', 'public', 'js', 'table', 'table-search.js'); const script = fs.readFileSync(scriptPath, 'utf8'); vm.runInNewContext(script, sandbox, { filename: scriptPath }); assert.equal(cardStyle.properties['--table-pagination-card-min-height'], '320px'); assert.equal(cardStyle.properties['--table-pagination-card-max-height'], '320px'); assert.deepEqual(bodyClassList.toggled, { name: 'table-pagination-page', value: true }); });