mirror of
https://github.com/screentinker/screentinker.git
synced 2026-06-17 03:32:32 -06:00
Each playlist item can carry schedule blocks (active days, start/end time-of-day, optional start/end dates). An item plays when the screen's local "now" matches at least one block; an item with no blocks always plays. #74 covers time-of-day/day-of-week windows including overnight wrap; #75 covers inclusive date ranges (auto-expiry). Evaluation is on-device, so dayparting and expiry work offline. - Shared evaluator contract: shared/schedule-vectors.json (39 vectors — DST US+AU, overnight-wrap anchoring, timezone correctness, date boundaries). Canonical JS evaluator in server/lib/schedule-eval.js; Kotlin and Tizen ports kept in lockstep by drift guards (Tizen byte-diff test, Kotlin JUnit reads the shared JSON, new android-test CI job). - All three players (web, Android, Tizen) filter by schedule against their own clock, idle with a "Nothing scheduled" message + 30s re-check when everything is filtered, and fail open on any evaluator error. - Editor: per-item schedule modal + row badge in the playlist editor; client validation mirrors the server; editing marks the playlist draft. - Part B (behaviour change): device/group schedule overrides now evaluate in each device's effective timezone instead of server-local time. - Device detail shows the reported timezone + a clock-skew warning. - i18n for en/es/fr/de/pt across all new strings (namespaced itemsched.* to avoid colliding with the device-schedule calendar's schedule.*). - CHANGELOG documents the feature, the Part B change, the fail-open guarantee, and the scheduled-single-video re-render tradeoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24 lines
1.2 KiB
JavaScript
24 lines
1.2 KiB
JavaScript
// Drives the canonical evaluator against the shared conformance vectors
|
|
// (shared/schedule-vectors.json). The same file is consumed by the Kotlin JUnit
|
|
// suite and the Tizen-JS-under-Node test, so all three implementations are held to
|
|
// one contract.
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert');
|
|
const path = require('node:path');
|
|
const fs = require('node:fs');
|
|
const { isItemActiveNow } = require('../lib/schedule-eval');
|
|
|
|
const vectorsPath = path.join(__dirname, '..', '..', 'shared', 'schedule-vectors.json');
|
|
const data = JSON.parse(fs.readFileSync(vectorsPath, 'utf8'));
|
|
|
|
test('schedule evaluator conforms to every shared vector', () => {
|
|
const failures = [];
|
|
for (const v of data.vectors) {
|
|
const got = isItemActiveNow(v.blocks, v.utc_now, v.timezone);
|
|
if (got !== v.expected) failures.push(` [${v.utc_now} ${v.timezone}] expected ${v.expected} got ${got} :: ${v.description}`);
|
|
}
|
|
if (failures.length) console.error('\n' + failures.join('\n'));
|
|
console.log(`schedule vectors: ${data.vectors.length - failures.length}/${data.vectors.length} passed`);
|
|
assert.strictEqual(failures.length, 0, `${failures.length} vector(s) failed`);
|
|
});
|