Files
luma-home/tests/sync-splash.test.mjs
T
Kaloyan DanchevandClaude Opus 4.6 cbea5a322f
CI / Identify a merged version PR (push) Canceled after 0s
CI / Rust checks, rendering, and native package (push) Canceled after 0s
CI / Publish release and Homebrew feed (push) Canceled after 0s
Release PR / Maintain Version Packages PR (push) Canceled after 0s
Merge Luma launcher fork into monorepo
Move trampoline files (boot-hook, watcher, overlay) into trampoline/
subdirectory. Add upstream Luma v2.3.1 source at repo root with our
additions:

- Page::Ha (4th nav tab) with placeholder empty state
- HA tab config schema and default entity mapping (ha-home/)
- Nav keys renamed: "home" → "start", new "ha" tab between Apps and Settings
- All state.rs/view.rs/main.rs/tests.rs updated for 4-tab navigation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-24 11:52:55 +03:00

143 lines
5.4 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import test from 'node:test';
import vm from 'node:vm';
const script = await fs.readFile(new URL('../tv/sync-splash.cjs', import.meta.url), 'utf8');
const app = 'com.ecklf.luma';
const state = '/var/lib/luma-home';
function fixture({ production = false, config, failWrite = false } = {}) {
const directory = `/media/${production ? 'cryptofs' : 'developer'}/apps/usr/palm/applications/${app}`;
const files = new Map([
[
`${directory}/appinfo.json`,
Buffer.from(JSON.stringify({ id: app, type: 'native', main: 'luma' })),
],
[`${directory}/splash.png`, Buffer.from('dunes')],
[`${directory}/splash/forest.png`, Buffer.from('forest')],
[`${directory}/splash/prism.png`, Buffer.from('prism')],
[`${state}/app/splash.png`, Buffer.from('old Home splash')],
]);
if (config) files.set(`${state}/state.json`, Buffer.from(JSON.stringify(config(directory))));
const writes = [];
const renames = [];
const missing = () => Object.assign(new Error('Missing'), { code: 'ENOENT' });
const mockFs = {
readFileSync(file, encoding) {
if (!files.has(file)) throw missing();
const data = files.get(file);
return encoding ? data.toString(encoding) : data;
},
writeFileSync(file, data, options) {
assert.ok(file.endsWith('/splash.png.new'));
assert.equal(options.mode, 0o644);
if (failWrite) throw Object.assign(new Error('Read-only filesystem'), { code: 'EROFS' });
writes.push(file);
files.set(file, data);
},
renameSync(from, to) {
assert.ok(files.has(from));
renames.push(to);
files.set(to, files.get(from));
files.delete(from);
},
};
return {
directory,
files,
writes,
renames,
sync(background, overrides = {}) {
const logs = [];
vm.runInNewContext(
script,
{
require(name) {
if (name === 'fs') return mockFs;
assert.equal(name, 'path', 'the helper must not invoke system or TV commands');
return path.posix;
},
process: { argv: ['node', JSON.stringify({ directory, background, ...overrides })] },
console: { log: (line) => logs.push(line) },
},
{ timeout: 2000 },
);
assert.deepEqual(logs.map(JSON.parse), [{ returnValue: true, background }]);
},
};
}
test('normal developer and production tile launches use the saved selection', () => {
for (const production of [false, true]) {
const mock = fixture({ production });
mock.sync('forest');
assert.equal(mock.files.get(`${mock.directory}/splash.png`).toString(), 'forest');
mock.sync('prism');
assert.equal(mock.files.get(`${mock.directory}/splash.png`).toString(), 'prism');
assert.deepEqual(mock.renames, [
`${mock.directory}/splash.png`,
`${mock.directory}/splash.png`,
]);
assert.ok(!mock.files.has(`${mock.directory}/splash.png.new`));
assert.equal(mock.files.get(`${state}/app/splash.png`).toString(), 'old Home splash');
}
});
test('only an enabled Home stage belonging to this installation is refreshed', () => {
for (const [enabled, matching] of [
[true, true],
[false, true],
[true, false],
]) {
const mock = fixture({
config: (directory) => ({ enabled, appDir: matching ? directory : '/another-installation' }),
});
mock.sync('prism');
assert.equal(mock.files.get(`${mock.directory}/splash.png`).toString(), 'prism');
assert.equal(
mock.files.get(`${state}/app/splash.png`).toString(),
enabled && matching ? 'prism' : 'old Home splash',
);
}
});
test('startup repairs a replaced package splash without rewriting an unchanged Home image', () => {
const mock = fixture({ config: (directory) => ({ enabled: true, appDir: directory }) });
mock.sync('forest');
mock.writes.length = 0;
mock.sync('forest');
assert.deepEqual(mock.writes, []);
mock.files.set(`${mock.directory}/splash.png`, Buffer.from('package update default'));
mock.sync('forest');
assert.deepEqual(mock.writes, [`${mock.directory}/splash.png.new`]);
assert.equal(mock.files.get(`${mock.directory}/splash.png`).toString(), 'forest');
});
test('missing sources and failed writes preserve the previous launch splash', () => {
for (const failWrite of [false, true]) {
const mock = fixture({ failWrite });
assert.throws(() => mock.sync(failWrite ? 'forest' : 'missing'), /Missing|Read-only/);
assert.equal(mock.files.get(`${mock.directory}/splash.png`).toString(), 'dunes');
assert.equal(mock.files.get(`${state}/app/splash.png`).toString(), 'old Home splash');
assert.deepEqual(mock.renames, []);
}
});
test('unrelated installations and invalid selections cannot update a launch splash', () => {
const mock = fixture();
for (const background of ['../forest', '', 'forest.png', null]) {
assert.throws(() => mock.sync(background), /Invalid splash background/);
}
for (const directory of ['/etc', `${mock.directory}/..`, `${mock.directory}-other`]) {
assert.throws(() => mock.sync('forest', { directory }), /Invalid Luma installation/);
}
mock.files.set(
`${mock.directory}/appinfo.json`,
Buffer.from(JSON.stringify({ id: 'another.app', type: 'native', main: 'luma' })),
);
assert.throws(() => mock.sync('forest'), /installed native Luma package/);
assert.deepEqual(mock.writes, []);
});