feat: add passenger information and settings components
- Introduced `PassengerInfo.tsx` for displaying trip details, including upcoming trips and fare information. - Added `PassengerSettings.tsx` for managing user-defined important places and fare settings. - Implemented validation functions for passenger settings in `passenger-settings.mjs`. - Created utility functions for transliterating stop names in `transliteration.ts`. - Developed weather hint functionality in `weather-hint.ts` to provide precipitation probabilities. - Established a mock server for testing passenger-related features in `passenger-server.mjs`. - Added comprehensive tests for passenger guide logic and transliteration functionality.
This commit is contained in:
@@ -8,6 +8,8 @@ const source = await readFile(new URL('../src/lib/storage.ts', import.meta.url),
|
||||
// Settings used by the actual board; no external project is required for these tests.
|
||||
const body = source.match(/export const defaultSettings: Settings = ([\s\S]*?);\nexport function/)[1];
|
||||
const defaults = Function(`return (${body})`)();
|
||||
// This fixture's device belongs to city 2; Polotsk tariffs must not leak into it.
|
||||
defaults.fareByType = [];
|
||||
const value = { state: 'paired', enabled: true, adminUrl: '', device: { id: 'test-device', name: 'Табло', cityId: 2, revision: 1, settings: defaults }, config: { configured: true, cityId: 2, cityName: 'Новополоцк', stopName: 'Остановка', defaultStopId: 1, refreshSeconds: 20, timeZone: 'Europe/Minsk' } };
|
||||
test('managed configuration validation rejects damaged, mismatched or unsupported settings', () => {
|
||||
assert.equal(isManagedState(value), true);
|
||||
@@ -18,6 +20,27 @@ test('managed configuration validation rejects damaged, mismatched or unsupporte
|
||||
assert.equal(isManagedState(legacy),true);assert.equal(hydrateManagedState(legacy).device.settings.nightMode,'auto');
|
||||
assert.equal(isManagedState({...value,device:{...value.device,settings:{...defaults,nightWakeMinutes:5}}}),false);
|
||||
});
|
||||
test('managed transliteration settings accept legacy devices and reject malformed flags', () => {
|
||||
const legacy = structuredClone(value); delete legacy.device.settings.journeyTranslitEnabled;
|
||||
assert.equal(isManagedState(legacy), true);
|
||||
assert.equal(hydrateManagedState(legacy).device.settings.journeyTranslitEnabled, false);
|
||||
const enabled = { ...value, device: { ...value.device, settings: { ...defaults, journeyTranslitEnabled: true } } };
|
||||
assert.equal(isManagedState(enabled), true);
|
||||
assert.equal(hydrateManagedState(enabled).device.settings.journeyTranslitEnabled, true);
|
||||
for (const flag of ['true', 1, null]) {
|
||||
assert.equal(isManagedState({ ...value, device: { ...value.device, settings: { ...defaults, journeyTranslitEnabled: flag } } }), false);
|
||||
}
|
||||
});
|
||||
test('managed passenger settings migrate without leaking another city tariff or accepting damaged arrays', () => {
|
||||
const legacy = structuredClone(value);
|
||||
for(const key of ['infoPlacesEnabled','infoFareEnabled','lastTripNoticeEnabled','weatherHintEnabled','places','fareRules','fareByType']) delete legacy.device.settings[key];
|
||||
assert.equal(isManagedState(legacy),true);assert.deepEqual(hydrateManagedState(legacy).device.settings.fareByType,[]);
|
||||
const polotsk={...legacy,device:{...legacy.device,cityId:1},config:{...legacy.config,cityId:1}};
|
||||
assert.equal(hydrateManagedState(polotsk).device.settings.fareByType[0].price,'1,05 BYN'); assert.equal(isManagedState(polotsk),true);
|
||||
for(const patch of [{places:null},{fareRules:[{routeId:1}]},{weatherHintEnabled:'true'},{fareByType:[{cityId:1,type:'bus',price:'1 BYN',payment:'Наличные',checkedAt:'2026-10-11'}]}]) {
|
||||
assert.equal(isManagedState({...value,device:{...value.device,settings:{...defaults,...patch}}}),false);
|
||||
}
|
||||
});
|
||||
test('pairing bridge keeps secrets in HttpOnly cookies, binds data to the individual device and rejects cross-site writes', async t => {
|
||||
const tokenA = 'A'.repeat(43), tokenB = 'B'.repeat(43), calls = [];
|
||||
let revoked = false;
|
||||
|
||||
Reference in New Issue
Block a user