mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Extract vessel components and preserve source lifecycle
This commit is contained in:
1 parent
8b3fad0672
commit
2f4839e8f2
23 files changed
+2967
-2182
No files matched your search
@@ -0,0 +1,163 @@
|
||||
import { DEFAULT_AIS_RUNTIME } from './policy.js';
|
||||
|
||||
export function createTesting({ vesselState, services, parts: components, layer, options }) {
|
||||
const { state } = vesselState;
|
||||
const aisLiveVesselsLayer = layer;
|
||||
|
||||
|
||||
/**
|
||||
* Bind the production interaction callbacks to mockable viewer/handler
|
||||
* surfaces. Test-only seam; behavior is shared with installInteraction().
|
||||
* @param {Object} viewer - Viewer-like object with scene.pick().
|
||||
* @param {Object} handler - Handler-like object with setInputAction().
|
||||
* @param {Object} keyTarget - EventTarget-like object with add/removeEventListener().
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function _bindVesselInteractionForTest(viewer, handler, keyTarget) {
|
||||
components.selection.bindVesselInteraction(viewer, handler, keyTarget);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prime the minimum live state needed by interaction/lifecycle wire tests.
|
||||
* @param {Object} [options={}] - Test state values.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function _setVesselStateForTest(options = {}) {
|
||||
components.lifecycle.resetState();
|
||||
const records = Array.isArray(options.records) ? options.records : [];
|
||||
state.viewer = options.viewer || null;
|
||||
state.enabled = options.enabled !== false;
|
||||
state.loaded = options.loaded === true;
|
||||
state.loading = options.loading === true;
|
||||
state.stale = options.stale === true;
|
||||
state.error = options.error || null;
|
||||
state.lastUpdate = options.lastUpdate ?? null;
|
||||
state.vesselRecords = records;
|
||||
state.count = records.length;
|
||||
state.vesselMap = new Map(
|
||||
records.filter((record) => record?.mmsi).map((record) => [record.mmsi, record])
|
||||
);
|
||||
state.selectedRecord = options.selectedRecord || null;
|
||||
state.billboardCollection = options.billboardCollection || { remove() {} };
|
||||
state.trail = options.trail || null;
|
||||
state.trailMmsi = options.trailMmsi || null;
|
||||
state.trailPositions = Array.isArray(options.trailPositions) ? [...options.trailPositions] : [];
|
||||
state.transportStatus = options.transportStatus || null;
|
||||
state.lastMessageAt = options.lastMessageAt ?? null;
|
||||
state.rawRowCount = Number.isFinite(options.rawRowCount) ? options.rawRowCount : 0;
|
||||
state.acceptedRowCount = Number.isFinite(options.acceptedRowCount) ? options.acceptedRowCount : records.length;
|
||||
state.firstConnectPhase = options.firstConnectPhase || 'idle';
|
||||
state.firstConnectStartedAt = options.firstConnectStartedAt ?? null;
|
||||
state.firstConnectDeadline = options.firstConnectDeadline ?? null;
|
||||
state.interactionHandlerFactory = options.interactionHandlerFactory || null;
|
||||
state.interactionKeyTarget = options.interactionKeyTarget || null;
|
||||
}
|
||||
|
||||
|
||||
/** Inject a host recorder for lifecycle/contract tests; null restores production. */
|
||||
|
||||
function _setVesselOverlayHostForTest(host = null) {
|
||||
vesselState._vesselOverlayHost = host || vesselState.DEFAULT_VESSEL_OVERLAY_HOST;
|
||||
}
|
||||
|
||||
|
||||
/** Exercise the production selector/publisher through a test-owned state. */
|
||||
|
||||
function _updateVesselCardsForTest(records = []) {
|
||||
components.rendering.updateClusteredLabels(records);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reconcile AIS rows through the production lifecycle. Test-only seam.
|
||||
* @param {Object} viewer - Viewer-like object.
|
||||
* @param {Array<Object>} rows - Raw AIS rows.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
function _reconcileVesselsForTest(viewer, rows) {
|
||||
components.store.reconcileVessels(viewer, rows);
|
||||
}
|
||||
|
||||
|
||||
/** Apply one server snapshot through the production pre-reconcile health gate. */
|
||||
|
||||
function _applyAisFeedSnapshotForTest(viewer, payload) {
|
||||
return components.ingestion.applyAisFeedSnapshot(viewer, payload);
|
||||
}
|
||||
|
||||
|
||||
/** Exercise the request-owned live loader with a test-controlled fetch. */
|
||||
|
||||
function _loadLivePositionsForTest(viewer) {
|
||||
return components.ingestion.loadLivePositions(viewer);
|
||||
}
|
||||
|
||||
|
||||
/** Start the production first-connect grace state without installing UI. */
|
||||
|
||||
function _beginAisSessionForTest() {
|
||||
components.lifecycle.beginAisSession();
|
||||
}
|
||||
|
||||
|
||||
/** Inject a deterministic clock/scheduler; null restores production runtime. */
|
||||
|
||||
function _setAisRuntimeForTest(runtime = null) {
|
||||
components.lifecycle.clearFirstConnectTimer();
|
||||
vesselState._aisRuntime = runtime
|
||||
? {
|
||||
now: runtime.now,
|
||||
setTimeout: runtime.setTimeout,
|
||||
clearTimeout: runtime.clearTimeout,
|
||||
}
|
||||
: DEFAULT_AIS_RUNTIME;
|
||||
}
|
||||
|
||||
|
||||
/** Read feed-health fields without exposing mutable production state. */
|
||||
|
||||
function _getVesselFeedStateForTest() {
|
||||
const stats = aisLiveVesselsLayer.getStats();
|
||||
return {
|
||||
count: state.count,
|
||||
loaded: state.loaded,
|
||||
loading: stats.loading,
|
||||
loadingLabel: stats.loadingLabel,
|
||||
stale: state.stale,
|
||||
error: state.error,
|
||||
status: stats.status,
|
||||
lastUpdate: state.lastUpdate,
|
||||
transportStatus: state.transportStatus,
|
||||
lastMessageAt: state.lastMessageAt,
|
||||
rawRowCount: state.rawRowCount,
|
||||
acceptedRowCount: state.acceptedRowCount,
|
||||
selectedMmsi: state.selectedRecord?.mmsi || null,
|
||||
trailMmsi: state.trailMmsi,
|
||||
trailPositionCount: state.trailPositions.length,
|
||||
sessionId: state.sessionId,
|
||||
firstConnectPhase: state.firstConnectPhase,
|
||||
firstConnectStartedAt: state.firstConnectStartedAt,
|
||||
firstConnectDeadline: state.firstConnectDeadline,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read lifecycle ownership state without exposing the mutable state object.
|
||||
* Test-only seam.
|
||||
* @returns {{trailMmsi: string|null, trailPositionCount: number, vesselCount: number}}
|
||||
*/
|
||||
|
||||
function _getVesselStateForTest() {
|
||||
return {
|
||||
trailMmsi: state.trailMmsi,
|
||||
trailPositionCount: state.trailPositions.length,
|
||||
vesselCount: state.vesselMap.size,
|
||||
};
|
||||
}
|
||||
return { _bindVesselInteractionForTest, _setVesselStateForTest, _setVesselOverlayHostForTest, _updateVesselCardsForTest, _reconcileVesselsForTest, _applyAisFeedSnapshotForTest, _loadLivePositionsForTest, _beginAisSessionForTest, _setAisRuntimeForTest, _getVesselFeedStateForTest, _getVesselStateForTest };
|
||||
}
|
||||
Reference in new issue
Block a user