Files
pulse-signage/test/font-library.test.js
lzstealth ddcc665f81
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m11s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 30s
Release v2.14.2
2026-10-12 00:56:09 +01:00

112 lines
6.0 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const {
buildFontFamilyFormats,
buildFontStylesheet,
collectFontLibrarySyncOperations
} = require('../src/web/lib/media/font-library');
test('buildFontFamilyFormats quotes multi-word font families for TinyMCE previews', () => {
const formats = buildFontFamilyFormats([
{ family: 'Old London', enabled: true },
{ family: 'My Custom Font', enabled: true }
]);
assert.match(formats, /Old London='Old London'/);
assert.match(formats, /My Custom Font='My Custom Font'/);
assert.match(formats, /Times New Roman='Times New Roman',Times,serif/);
assert.match(formats, /Lucida Sans Unicode='Lucida Sans Unicode','Lucida Grande',sans-serif/);
});
test('buildFontStylesheet keeps disabled fonts available for rendering', () => {
const stylesheet = buildFontStylesheet([
{ family: 'Enabled Font', fileName: 'enabled-font.ttf', format: 'truetype', enabled: true },
{ family: 'Disabled Font', fileName: 'disabled-font.ttf', format: 'truetype', enabled: false }
]);
assert.match(stylesheet, /Enabled Font/);
assert.match(stylesheet, /Disabled Font/);
assert.match(stylesheet, /\/media\/assets\/fonts\/enabled-font.ttf/);
});
test('legacy fonts and weather icons migrate into shared assets without losing metadata', async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pulse-assets-'));
try {
const fonts = path.join(root, 'fonts');
const weather = path.join(root, 'weather-icons', 'abc');
await fs.promises.mkdir(fonts, { recursive: true });
await fs.promises.mkdir(weather, { recursive: true });
await fs.promises.writeFile(path.join(fonts, 'custom.ttf'), 'font-data');
await fs.promises.writeFile(path.join(fonts, 'fonts.json'), JSON.stringify([{ id: 'custom', family: 'Custom', fileName: 'custom.ttf', enabled: false }]));
await fs.promises.writeFile(path.join(fonts, 'fonts.css'), "src:url('/media/fonts/custom.ttf')");
await fs.promises.writeFile(path.join(weather, 'clear-123abc.png'), 'icon-data');
await fs.promises.writeFile(path.join(root, 'weather-icons', 'sets.json'), JSON.stringify([{ id: 'abc', name: 'Custom', icons: { clear: '/media/weather-icons/abc/clear-123abc.png' } }]));
const library = require('../src/web/lib/media/font-library');
await library.ensureFontLibrary(path.join(root, 'uploads'));
const loaded = library.loadFontLibrary(root);
assert.equal(loaded.fonts[0].id, 'custom');
assert.equal(loaded.fonts[0].enabled, false);
assert.equal(loaded.manifestPath, path.join(root, 'assets', 'fonts.json'));
assert.equal(loaded.stylesheetPath, path.join(root, 'assets', 'fonts.css'));
assert.match(loaded.stylesheetHref, /^\/media\/assets\/fonts.css/);
assert.equal(fs.readFileSync(path.join(root, 'assets', 'fonts', 'custom.ttf'), 'utf8'), 'font-data');
assert.match(fs.readFileSync(loaded.stylesheetPath, 'utf8'), /\/media\/assets\/fonts\/custom.ttf/);
const weatherLibrary = require('../src/web/lib/media/weather-icon-library');
assert.equal(weatherLibrary.readWeatherIconSets(root)[0].icons.clear, '/media/assets/weather-icons/abc/clear-123abc.png');
assert.equal(weatherLibrary.getWeatherManifestPath(root), path.join(root, 'assets', 'weather.json'));
assert.equal(fs.existsSync(path.join(root, 'assets', 'weather-icons', 'sets.json')), false);
assert.equal(fs.readFileSync(path.join(root, 'assets', 'weather-icons', 'abc', 'clear-123abc.png'), 'utf8'), 'icon-data');
assert.equal(fs.existsSync(fonts), false);
assert.equal(fs.existsSync(path.join(root, 'weather-icons')), false);
await library.ensureFontLibrary(root);
assert.equal(library.loadFontLibrary(root).fonts.length, 1);
} finally {
await fs.promises.rm(root, { recursive: true, force: true });
}
});
test('shared assets migration tolerates simultaneous service startup', async () => {
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pulse-assets-race-'));
try {
const fonts = path.join(root, 'fonts');
await fs.promises.mkdir(fonts);
await fs.promises.writeFile(path.join(fonts, 'fonts.json'), '[]');
await fs.promises.writeFile(path.join(fonts, 'fonts.css'), "src:url('/media/fonts/custom.ttf')");
await Promise.all(Array.from({ length: 100 }, function (_unused, index) {
return fs.promises.writeFile(path.join(fonts, 'font-' + index + '.ttf'), 'font-data');
}));
const run = require('node:util').promisify(require('node:child_process').execFile);
const modulePath = require.resolve('../src/web/lib/media/assets-storage');
await Promise.all(Array.from({ length: 4 }, function () {
return run(process.execPath, ['-e', 'require(' + JSON.stringify(modulePath) + ').migrateLegacyAssets(' + JSON.stringify(root) + ')']);
}));
assert.equal(fs.existsSync(fonts), false);
assert.equal(fs.readdirSync(path.join(root, 'assets', 'fonts')).length, 100);
assert.equal(JSON.parse(fs.readFileSync(path.join(root, 'assets', 'fonts.json'))).length, 0);
} finally {
await fs.promises.rm(root, { recursive: true, force: true });
}
});
test('collectFontLibrarySyncOperations keeps disabled fonts as puts', async () => {
const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'pulse-font-library-'));
const fontDir = path.join(tempRoot, 'fonts');
await fs.promises.mkdir(fontDir, { recursive: true });
await fs.promises.writeFile(path.join(fontDir, 'fonts.json'), JSON.stringify([
{ family: 'Enabled Font', fileName: 'enabled-font.ttf', enabled: true },
{ family: 'Disabled Font', fileName: 'disabled-font.ttf', enabled: false }
], null, 2));
const operations = collectFontLibrarySyncOperations(tempRoot);
const fontOps = operations.filter(function (operation) {
return String(operation.uploadPath || '').indexOf('/media/assets/fonts/') === 0 && /\.ttf$/.test(String(operation.uploadPath || ''));
});
assert.deepEqual(fontOps.map(function (operation) {
return operation.type;
}), ['put', 'put']);
});