import test from 'node:test'; import assert from 'node:assert/strict'; import { colorScheme, colorSchemes, lightColorSchemes, departurePage, durationParts, numericFrame, stopDirection } from '../src/lib/presentation.mjs'; test('all accent schemes remain legible against black and with black route numbers', () => { for (const scheme of colorSchemes) { const rgb = scheme.color.slice(1).match(/../g).map(value => parseInt(value, 16) / 255); const channels = rgb.map(value => value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4); const luminance = .2126 * channels[0] + .7152 * channels[1] + .0722 * channels[2]; assert.ok((luminance + .05) / .05 >= 7, scheme.id); } assert.equal(colorScheme('unknown').id, 'lime'); }); test('light accents have at least 7:1 contrast against white and retain a separate default', () => { for (const scheme of lightColorSchemes) { const rgb = scheme.color.slice(1).match(/../g).map(value => parseInt(value, 16) / 255); const channels = rgb.map(value => value <= .04045 ? value / 12.92 : ((value + .055) / 1.055) ** 2.4); const luminance = .2126 * channels[0] + .7152 * channels[1] + .0722 * channels[2]; assert.ok(1.05 / (luminance + .05) >= 7, scheme.id); } assert.equal(colorScheme('lime', 'light').id, 'blue'); assert.equal(colorScheme('cyan').id, 'cyan'); }); test('waiting time crosses hour boundaries without displaying hundreds of minutes', () => { assert.deepEqual(durationParts(59), [{ value: 59, unit: 'мин' }]); assert.deepEqual(durationParts(60), [{ value: 1, unit: 'ч' }]); assert.deepEqual(durationParts(121), [{ value: 2, unit: 'ч' }, { value: 1, unit: 'мин' }]); assert.deepEqual(durationParts(255), [{ value: 4, unit: 'ч' }, { value: 15, unit: 'мин' }]); assert.deepEqual(durationParts(302), [{ value: 5, unit: 'ч' }, { value: 2, unit: 'мин' }]); }); test('clock carry rolls changed positions while keeping separators and unchanged digits still', () => { const frame = numericFrame('09:59', '10:00', 959, 1000); assert.equal(frame.direction, 'up'); assert.deepEqual(frame.glyphs.filter(glyph => glyph.changed).map(glyph => glyph.position), [4, 3, 1, 0]); assert.equal(frame.glyphs.find(glyph => glyph.character === ':').changed, false); assert.equal(numericFrame('12', '11', 12, 11).direction, 'down'); assert.equal(numericFrame('10', '9', 10, 9).glyphs.at(-1).character, '9'); }); test('six departures fit in one page when space permits; short screens preserve all requested departures across balanced pages', () => { const items = Array.from({ length: 8 }, (_, id) => ({ id })); const full = departurePage(items, 6, 6, 0); assert.equal(full.items.length, 6); assert.equal(full.pages, 1); const first = departurePage(items, 6, 4, 0), second = departurePage(items, 6, 4, 1); assert.deepEqual([...first.items, ...second.items].map(item => item.id), [0, 1, 2, 3, 4, 5]); assert.equal(first.items.length, second.items.length); }); test('direction comes from platform metadata without inventing one for the main platform', () => { assert.equal(stopDirection({ direction: 'в Новополоцк' }), 'В Новополоцк'); assert.equal(stopDirection({ direction: 'основная' }), ''); assert.equal(stopDirection({ direction: '' }), ''); });