mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-15 08:53:43 +08:00
7b84b4e621
VASM/PiP previews fixed with shared camera lifecycle, timeouts and automatic recovery. Theme Maker side cameras now work independently. VASM handles warning borders only; PiP handles cameras and saved layouts. Refresh/Retry controls removed in favor of automatic state updates. Disconnect/loading states now persist correctly and recover automatically. Settings sync and shared-write propagation restored. Toggle/editor and Vehicle Default flicker fixed. Default actions simplified and numeric Default restored. Sidebar and Tools now share one catalog. Notices and form controls standardized. Recordings UI improved with search, sorting and preservation filters. Playback supports full-resolution cameras and automatic segment advancement. Recording downloads and management restored. Recording status now reflects actual scan state. Map partial-recovery errors fixed. Regression coverage expanded across browser, camera, polling, recording and placement flows.
82 lines
5.6 KiB
JavaScript
82 lines
5.6 KiB
JavaScript
import { remainingLocationLease } from '../web/js/navigation.js'
|
|
import assert from 'node:assert/strict'
|
|
import { project, unproject, relativeX, RasterMap } from '../web/js/navigation-map.js'
|
|
for (const zoom of [0, 2, 13, 18]) {
|
|
const point = { latitude: 40, longitude: -90 }
|
|
const roundTrip = unproject(...project(point, zoom), zoom)
|
|
assert.ok(Math.abs(roundTrip.latitude - point.latitude) < 1e-10)
|
|
assert.ok(Math.abs(roundTrip.longitude - point.longitude) < 1e-10)
|
|
}
|
|
assert.equal(relativeX(5, 1010, 1024), 1029)
|
|
assert.ok(project({ latitude: 90, longitude: 0 }, 2).every(Number.isFinite))
|
|
const calls = [], listeners = new Map()
|
|
globalThis.ResizeObserver = class { observe() {} disconnect() { calls.push('disconnect') } }
|
|
const ctx = new Proxy({}, { get: (obj, key) => obj[key] || (() => {}) })
|
|
const canvas = { clientWidth: 400, clientHeight: 360, getContext: () => ctx, setPointerCapture() {}, addEventListener: (name, fn) => listeners.set(name, fn), removeEventListener: name => listeners.delete(name) }
|
|
const map = new RasterMap(canvas)
|
|
map.load = () => {}
|
|
map.update({ destination: { latitude: 40, longitude: -90 }, route: [{ latitude: 40, longitude: -90 }, { latitude: 40.01, longitude: -89.99 }] }, false)
|
|
const before = project(map.center, map.zoom)
|
|
listeners.get('pointerdown')({ clientX: 100, clientY: 100, pointerId: 1 })
|
|
listeners.get('pointermove')({ clientX: 150, clientY: 120 })
|
|
assert.ok(Math.abs(project(map.center, map.zoom)[0] - before[0] + 50) < 1e-6)
|
|
listeners.get('pointerup')({})
|
|
const panned = { ...map.center }
|
|
map.update(map.data, false)
|
|
assert.deepEqual(map.center, panned)
|
|
map.changeZoom(1); assert.equal(map.zoom, 3)
|
|
map.recenter(); assert.equal(map.zoom, 2)
|
|
map.close(); assert.equal(listeners.size, 0); assert.deepEqual(calls, ['disconnect'])
|
|
console.log('Map projection, dateline, pan, zoom, recenter, poll stability and cleanup passed')
|
|
const timers = new Map(); let next = 0
|
|
const savedSet = globalThis.setTimeout, savedClear = globalThis.clearTimeout
|
|
globalThis.setTimeout = (fn, ms) => { timers.set(++next, {fn, ms}); return next }
|
|
globalThis.clearTimeout = id => timers.delete(id)
|
|
const leased = new RasterMap(canvas); leased.load = () => {}
|
|
leased.update({ location: {latitude:40,longitude:-90,validForMs:100} }, false)
|
|
assert.equal(leased.locationFresh, true); assert.ok(Math.abs([...timers.values()][0].ms - 100) < 1e-6)
|
|
leased.update({ location: {latitude:41,longitude:-89,bearing:125,validForMs:200} }, false)
|
|
assert.equal(timers.size, 1); [...timers.values()][0].fn(); assert.equal(leased.locationFresh, false)
|
|
leased.update({ location: {latitude:41,longitude:-89,validForMs:0} }, false)
|
|
assert.equal(leased.locationFresh, false); assert.equal(timers.size, 0)
|
|
leased.update({location:null}, false); assert.equal(leased.lastLocation.latitude,41); assert.equal(leased.lastLocation.bearing,125);
|
|
leased.recenter(); assert.equal(leased.center.latitude,41); assert.equal(leased.locationFresh,false);
|
|
leased.close(); globalThis.setTimeout = savedSet; globalThis.clearTimeout = savedClear
|
|
console.log('GPS lease expiry, replacement, delayed-expired response and teardown passed')
|
|
|
|
assert.equal(remainingLocationLease(2000,100,600),1500)
|
|
assert.equal(remainingLocationLease(2000,100,2200),0)
|
|
const actualPerformance = globalThis.performance
|
|
let now = 1000
|
|
globalThis.performance = { now: () => now }
|
|
globalThis.setTimeout = (fn, ms) => { timers.set(++next, {fn, ms}); return next }
|
|
globalThis.clearTimeout = id => timers.delete(id)
|
|
const samePayload = { location: {latitude:40,longitude:-90,validForMs:100} }
|
|
const stableLease = new RasterMap(canvas); stableLease.load = () => {}
|
|
stableLease.update(samePayload, false); now += 60
|
|
stableLease.update(samePayload, true); assert.equal(stableLease.locationFresh, false)
|
|
stableLease.update(samePayload, false); assert.equal([...timers.values()][0].ms, 40)
|
|
now += 41; stableLease.update(samePayload, false); assert.equal(stableLease.locationFresh, false)
|
|
stableLease.close(); timers.clear()
|
|
const realFetch = globalThis.fetch
|
|
let requests = 0, message = ''
|
|
globalThis.fetch = (_, options) => { requests++; return new Promise((resolve, reject) => options.signal.addEventListener('abort', () => reject(new Error('aborted')))) }
|
|
const deadlineMap = new RasterMap(canvas, value => { message = value }); deadlineMap.draw = () => {}
|
|
const pendingLoad = deadlineMap.load('0/0/0')
|
|
const deadline = [...timers.values()].find(timer => timer.ms === 8000)
|
|
deadline.fn(); await pendingLoad
|
|
assert.equal(deadlineMap.failed.has('0/0/0'), true); assert.ok(message.includes('Reconnecting')); assert.equal(requests, 1)
|
|
deadlineMap.close(); timers.clear()
|
|
globalThis.fetch = realFetch; globalThis.performance = actualPerformance; globalThis.setTimeout = savedSet; globalThis.clearTimeout = savedClear
|
|
console.log('Same-payload stale recovery cannot extend GPS lease; tile deadline requires explicit Retry')
|
|
|
|
const restored = new RasterMap(canvas); restored.load = () => {}
|
|
restored.update({location:{latitude:42,longitude:-88,bearing:170,lastKnown:true,validForMs:0}}, false)
|
|
assert.equal(restored.center.latitude,42); assert.equal(restored.zoom,15); assert.equal(restored.locationFresh,false)
|
|
const savedCenter = {...restored.center}
|
|
restored.update({location:null},true); assert.deepEqual(restored.center,savedCenter)
|
|
restored.update({location:{latitude:43,longitude:-87,validForMs:100}},false)
|
|
assert.equal(restored.center.latitude,43); assert.equal(restored.lastLocation.latitude,43); assert.equal(restored.lastLocation.bearing,170); assert.equal(restored.locationFresh,true)
|
|
restored.close()
|
|
console.log('Durable last-known context initializes map without a live lease; reconnect and new live fix retain bearing')
|