Files
luma-home/tests/brands-smoke.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

88 lines
3.1 KiB
JavaScript

// Exercise every curated logo with the native desktop renderer. No TV, network
// service or weather API is used. The contact sheet is a manual design preview.
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import sharp from 'sharp';
const root = path.resolve(import.meta.dirname, '..');
const out = path.join(root, '.build');
await fs.mkdir(out, { recursive: true });
function run(command, args) {
const result = spawnSync(command, args, { cwd: root, stdio: 'inherit', timeout: 120000 });
if (result.error) throw result.error;
assert.equal(result.status, 0, `${command} failed`);
}
run('cargo', ['build', '--locked', '--package', 'luma', '--no-default-features']);
const binary = path.resolve(root, process.env.CARGO_TARGET_DIR ?? 'target', 'debug/luma');
const scratch = await fs.mkdtemp(path.join(out, 'brand-gallery-'));
try {
const preferences = path.join(scratch, 'preferences.json');
const dump = path.join(scratch, 'state.json');
const diagnostics = path.join(scratch, 'render.json');
const screenshot = path.join(scratch, 'card.png');
await fs.writeFile(preferences, JSON.stringify({ reduced_motion: true, show_weather: false }));
run(binary, [
'--preferences',
preferences,
'--script',
'apps',
'--frames',
'18',
'--dump-state',
dump,
]);
const { inventory } = JSON.parse(await fs.readFile(dump, 'utf8'));
const logos = (await fs.readdir(path.join(root, 'assets/logos')))
.filter((file) => file.endsWith('.svg'))
.map((file) => path.parse(file).name)
.sort();
const cards = [];
for (const [index, logo] of logos.entries()) {
const app = inventory.find((app) => app.brand === logo);
assert.ok(app, `Desktop demo is missing ${logo}`);
run(binary, [
'--preferences',
preferences,
'--script',
`apps,type:${app.title},back`,
'--frames',
'18',
'--screenshot',
screenshot,
'--dump-state',
dump,
'--diagnostics',
diagnostics,
]);
const state = JSON.parse(await fs.readFile(dump, 'utf8'));
const report = JSON.parse(await fs.readFile(diagnostics, 'utf8'));
assert.ok(state.apps.includes(app.id), `${logo} must be searchable in the demo`);
assert.equal(report.capture.missing_images, 0, `Missing artwork for ${logo}`);
assert.equal(report.capture.glyph_failures, 0, `Invalid caption for ${logo}`);
assert.ok(report.capture.vector_meshes > 0);
const input = await sharp(screenshot)
.extract({ left: 78, top: 264, width: 282, height: 238 })
.png()
.toBuffer();
cards.push({ input, left: (index % 6) * 282, top: Math.floor(index / 6) * 238 });
}
await sharp({
create: {
width: 6 * 282,
height: Math.ceil(cards.length / 6) * 238,
channels: 4,
background: '#11151b',
},
})
.composite(cards)
.png()
.toFile(path.join(out, 'brands-gallery.png'));
console.log(
`All ${logos.length} SVG-backed demo apps rendered. Contact sheet: .build/brands-gallery.png`,
);
} finally {
await fs.rm(scratch, { recursive: true, force: true });
}