Files
houseplan-card/test/moon-settings.test.mjs
T
Sergey MatyuninandClaude Opus 5.5 c8c470ed57 feat(view): the moon in its phase over the "Follow the Sun" background (#661)
At dawn, dusk and night the environment shows the moon in the top-left
corner of the scene, behind the plan: computed in the card from the home
coordinates and the browser clock (short Meeus series + topocentric
parallax, within 1.4° / 1.8 pp of JPL Horizons), one designer image under
a continuous phase mask with the lit side always on the left (owner
2026-09-29), a feathered terminator, 3°/3 % thresholds and the 2 s fade of
the window rays. Everything but a small gate lives in the lazy
moon-runtime chunk, with its own 30 s ticker. General settings: "Sun"
becomes "Sun and Moon" with one switch, on for new installations
(DEFAULT_CONFIG), off for existing ones.

The initial View graph sat 728 B under its budget: the gate is paid for by
moving fifteen dialog-only strings of General settings into the lazy
settings dictionary (#459) and by one build fingerprint literal instead of
three, so the graph ends 4 B above dev. Golden: two new moon scenes, and
the two General settings help frames show «Sun and Moon»; the WSL artifact
test fixture now models scenes whose first capture awaits acceptance.

Issue: #661
User-Visible: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 12:25:46 +03:00

54 lines
3.0 KiB
JavaScript

// #661 AC10: the moon switch in General settings — «Sun and Moon» section,
// reset to on, save writes `true` or removes the key; its strings exist in all
// four dictionaries. The dialog is a lazy Lit module (its layout is read as a
// source contract); reading and writing the key are executed.
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { moonDraftOf, writeMoonSetting } from '../test-build/editors/general-form-state.js';
const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8');
const json = (path) => JSON.parse(read(path));
test('#661 AC10: the switch sits in the «Sun and Moon» card under «Sunlight through windows»', () => {
const dialog = read('src/editors/general-settings-dialog.ts');
const sun = dialog.slice(dialog.indexOf("id: 'sun',"), dialog.indexOf("id: 'data',"));
assert.match(sun, /title: t\('gs\.sun_group'\)/);
const rays = sun.indexOf("id: 'gs-sun-rays'");
const moon = sun.indexOf("id: 'gs-moon'");
assert.ok(rays > 0 && moon > rays, 'moon row follows the sun-rays row');
const row = sun.slice(moon, sun.indexOf('})}', moon));
assert.match(row, /icon: 'mdi:moon-waning-crescent'/);
assert.match(row, /title: st\('gs\.moon'\), caption: st\('gs\.moon_hint'\)/);
assert.match(row, /checked: d\.moon, onChange: \(v\) => set\(\{ moon: v \}\)/);
const reset = dialog.slice(dialog.indexOf("${t('gs.reset')}") - 600, dialog.indexOf("${t('gs.reset')}"));
assert.match(reset, /moon: true/, 'reset to defaults switches the moon on (new installations)');
});
test('#661 AC10: opening reads only an explicit true; saving writes true or removes the key', () => {
assert.equal(moonDraftOf({ moon: true }), true);
for (const settings of [undefined, null, {}, { moon: false }, { moon: 'true' }, { moon: 1 }]) {
assert.equal(moonDraftOf(settings), false, JSON.stringify(settings));
}
const on = { bg_mode: 'daynight' };
writeMoonSetting(on, true);
assert.deepEqual(on, { bg_mode: 'daynight', moon: true });
const off = { bg_mode: 'daynight', moon: true };
writeMoonSetting(off, false);
assert.deepEqual(off, { bg_mode: 'daynight' });
assert.equal('moon' in off, false, 'false is stored as absence');
});
test('#661 AC10: strings in all four dictionaries, section renamed', () => {
const group = { en: 'Sun and Moon', ru: 'Солнце и Луна', de: 'Sonne und Mond', fr: 'Soleil et Lune' };
for (const lang of ['en', 'ru', 'de', 'fr']) {
assert.equal(json(`src/i18n/${lang}.json`)['gs.sun_group'], group[lang], lang);
const settings = json(`src/i18n/settings/${lang}.json`);
for (const key of ['gs.moon', 'gs.moon_hint']) {
assert.ok(typeof settings[key] === 'string' && settings[key].trim(), `${lang} ${key}`);
}
}
assert.equal(json('src/i18n/settings/ru.json')['gs.moon'], 'Луна на плане в сумерках и ночью');
assert.equal(json('src/i18n/settings/en.json')['gs.moon'], 'Moon over the plan at dusk and night');
});