gps-plus-slam-app-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +222 -0
- package/dist/alignment-lerper-D9BeDwZX.d.ts +29 -0
- package/dist/app-selectors-DyMzxCEC.d.ts +140 -0
- package/dist/ar/camera-blit-capture.d.ts +2 -0
- package/dist/ar/camera-blit-capture.js +253 -0
- package/dist/ar/capture-failure-tracker.d.ts +2 -0
- package/dist/ar/capture-failure-tracker.js +45 -0
- package/dist/ar/chromium-camera-access-workaround.d.ts +2 -0
- package/dist/ar/chromium-camera-access-workaround.js +32 -0
- package/dist/ar/depth-sampler.d.ts +3 -0
- package/dist/ar/depth-sampler.js +147 -0
- package/dist/ar/image-capture.d.ts +2 -0
- package/dist/ar/image-capture.js +152 -0
- package/dist/ar/index.d.ts +12 -0
- package/dist/ar/index.js +12 -0
- package/dist/ar/replay-scene.d.ts +2 -0
- package/dist/ar/replay-scene.js +325 -0
- package/dist/ar/scene-node-names.d.ts +2 -0
- package/dist/ar/scene-node-names.js +16 -0
- package/dist/ar/tracking-state.d.ts +2 -0
- package/dist/ar/tracking-state.js +164 -0
- package/dist/ar/webxr-session.d.ts +4 -0
- package/dist/ar/webxr-session.js +890 -0
- package/dist/ar/xr-camera-texture.d.ts +2 -0
- package/dist/ar/xr-camera-texture.js +39 -0
- package/dist/ar/xr-error-handler.d.ts +2 -0
- package/dist/ar/xr-error-handler.js +37 -0
- package/dist/ar-types-B-ORgk6Z.d.ts +82 -0
- package/dist/camera-blit-capture-DHuyrrxr.d.ts +108 -0
- package/dist/camera-follower-BMqPddxx.d.ts +29 -0
- package/dist/capture-failure-tracker-2Q6oI2uQ.d.ts +52 -0
- package/dist/chromium-camera-access-workaround-CU5zSKNr.d.ts +61 -0
- package/dist/community-license-key-ChZLCJyO.d.ts +21 -0
- package/dist/concurrency-p6gB30fT.d.ts +24 -0
- package/dist/css3d-renderer-manager-rgLMVAvd.d.ts +23 -0
- package/dist/depth-sampler-BNCtnEoP.d.ts +108 -0
- package/dist/failure-tracker-1sQ_Cgyw.d.ts +49 -0
- package/dist/file-system-BsQgYCU3.d.ts +128 -0
- package/dist/file-system-utils-5cWO8Xyv.d.ts +24 -0
- package/dist/format-file-size-onGq0k1Q.d.ts +24 -0
- package/dist/fused-path-DtqCSqJh.d.ts +25 -0
- package/dist/geo-types-Xx-LVcnw.d.ts +29 -0
- package/dist/gps-Ddzr2KlX.d.ts +54 -0
- package/dist/gps-compass-cubes-C-6JEnpR.d.ts +22 -0
- package/dist/gps-error-handler-VPuNOuf8.d.ts +40 -0
- package/dist/gps-event-markers-DpEy2qRd.d.ts +67 -0
- package/dist/h3-ref-point-CFhayNSZ.d.ts +59 -0
- package/dist/image-capture-2pKjYUS6.d.ts +130 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +61 -0
- package/dist/leaflet-map-overlay-CvfQXTye.d.ts +114 -0
- package/dist/lerp-utils-87NWjUOD.d.ts +20 -0
- package/dist/licensing/community-license-key.d.ts +2 -0
- package/dist/licensing/community-license-key.js +21 -0
- package/dist/licensing/index.d.ts +2 -0
- package/dist/licensing/index.js +2 -0
- package/dist/list-formatter-DKQQVLcl.d.ts +14 -0
- package/dist/logger-B81iwxx0.d.ts +74 -0
- package/dist/map-overlay-DuiZuCTx.d.ts +153 -0
- package/dist/null-storage-backend-CsWvQwN8.d.ts +11 -0
- package/dist/opfs-storage-ChOzF_Wp.d.ts +181 -0
- package/dist/opfs-storage-backend-CVcmfJHN.d.ts +11 -0
- package/dist/permission-checker-jO2ziCWC.d.ts +146 -0
- package/dist/persistence-middleware-DrptY02i.d.ts +31 -0
- package/dist/recorder-slice-fcwAXrsh.d.ts +35 -0
- package/dist/recording-coordinator-DzflP07_.d.ts +81 -0
- package/dist/recording-options-BYUFX8iU.d.ts +166 -0
- package/dist/recording-replayer-oOzPITrd.d.ts +32 -0
- package/dist/ref-point-importer-B_wn5ABk.d.ts +62 -0
- package/dist/ref-point-loader-Pt1smgHS.d.ts +117 -0
- package/dist/ref-points/h3-ref-point.d.ts +2 -0
- package/dist/ref-points/h3-ref-point.js +74 -0
- package/dist/ref-points/index.d.ts +2 -0
- package/dist/ref-points/index.js +2 -0
- package/dist/ref-points-slice-tC-Wd6XJ.d.ts +48 -0
- package/dist/reference-points-CFWy8_wj.d.ts +68 -0
- package/dist/replay-engine-DuX316ae.d.ts +103 -0
- package/dist/replay-scene-DtUm6bsr.d.ts +109 -0
- package/dist/routing-slice-BlHTM8eh.d.ts +23 -0
- package/dist/scene-node-names-DPsaE2z3.d.ts +14 -0
- package/dist/sensors/gps-error-handler.d.ts +2 -0
- package/dist/sensors/gps-error-handler.js +53 -0
- package/dist/sensors/gps.d.ts +2 -0
- package/dist/sensors/gps.js +93 -0
- package/dist/sensors/index.d.ts +4 -0
- package/dist/sensors/index.js +4 -0
- package/dist/sensors/permission-checker.d.ts +2 -0
- package/dist/sensors/permission-checker.js +397 -0
- package/dist/state/app-selectors.d.ts +2 -0
- package/dist/state/app-selectors.js +35 -0
- package/dist/state/index.d.ts +17 -0
- package/dist/state/index.js +13 -0
- package/dist/state/persistence-middleware.d.ts +2 -0
- package/dist/state/persistence-middleware.js +71 -0
- package/dist/state/recorder-slice.d.ts +2 -0
- package/dist/state/recorder-slice.js +47 -0
- package/dist/state/recording-coordinator.d.ts +2 -0
- package/dist/state/recording-coordinator.js +150 -0
- package/dist/state/recording-options.d.ts +2 -0
- package/dist/state/recording-options.js +192 -0
- package/dist/state/recording-replayer.d.ts +2 -0
- package/dist/state/recording-replayer.js +39 -0
- package/dist/state/ref-points-slice.d.ts +2 -0
- package/dist/state/ref-points-slice.js +44 -0
- package/dist/state/replay-engine.d.ts +2 -0
- package/dist/state/replay-engine.js +230 -0
- package/dist/state/routing-slice.d.ts +2 -0
- package/dist/state/routing-slice.js +13 -0
- package/dist/state/store-subscribers.d.ts +3 -0
- package/dist/state/store-subscribers.js +97 -0
- package/dist/state/store.d.ts +9 -0
- package/dist/state/store.js +71 -0
- package/dist/state/subscribe-to-selector.d.ts +2 -0
- package/dist/state/subscribe-to-selector.js +26 -0
- package/dist/storage/file-system-utils.d.ts +2 -0
- package/dist/storage/file-system-utils.js +31 -0
- package/dist/storage/file-system.d.ts +2 -0
- package/dist/storage/file-system.js +229 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +10 -0
- package/dist/storage/null-storage-backend.d.ts +2 -0
- package/dist/storage/null-storage-backend.js +8 -0
- package/dist/storage/opfs-storage-backend.d.ts +2 -0
- package/dist/storage/opfs-storage-backend.js +15 -0
- package/dist/storage/opfs-storage.d.ts +2 -0
- package/dist/storage/opfs-storage.js +242 -0
- package/dist/storage/ref-point-importer.d.ts +2 -0
- package/dist/storage/ref-point-importer.js +163 -0
- package/dist/storage/ref-point-loader.d.ts +2 -0
- package/dist/storage/ref-point-loader.js +248 -0
- package/dist/storage/ref-point-recovery.d.ts +28 -0
- package/dist/storage/ref-point-recovery.js +159 -0
- package/dist/storage/storage-backend.d.ts +2 -0
- package/dist/storage/storage-backend.js +1 -0
- package/dist/storage/zip-export.d.ts +2 -0
- package/dist/storage/zip-export.js +214 -0
- package/dist/storage/zip-reader.d.ts +2 -0
- package/dist/storage/zip-reader.js +190 -0
- package/dist/storage-backend-BcEItnFr.d.ts +20 -0
- package/dist/store-C7PKOj8S.d.ts +96 -0
- package/dist/store-subscribers-CuoIzB5T.d.ts +77 -0
- package/dist/subscribe-to-selector-UTZ0KkAc.d.ts +26 -0
- package/dist/test-utils/browser-mocks.d.ts +294 -0
- package/dist/test-utils/browser-mocks.js +568 -0
- package/dist/test-utils/zip-round-trip-helpers.d.ts +60 -0
- package/dist/test-utils/zip-round-trip-helpers.js +207 -0
- package/dist/three-dispose-CCBHjE_a.d.ts +30 -0
- package/dist/tracking-state-B9QfuZCg.d.ts +101 -0
- package/dist/types/ar-types.d.ts +2 -0
- package/dist/types/ar-types.js +1 -0
- package/dist/types/geo-types.d.ts +2 -0
- package/dist/types/geo-types.js +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/concurrency.d.ts +2 -0
- package/dist/utils/concurrency.js +40 -0
- package/dist/utils/failure-tracker.d.ts +2 -0
- package/dist/utils/failure-tracker.js +48 -0
- package/dist/utils/format-file-size.d.ts +2 -0
- package/dist/utils/format-file-size.js +41 -0
- package/dist/utils/fused-path.d.ts +2 -0
- package/dist/utils/fused-path.js +44 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/list-formatter.d.ts +2 -0
- package/dist/utils/list-formatter.js +17 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +226 -0
- package/dist/vis-colors-Qi2vlRGt.d.ts +63 -0
- package/dist/visualization/alignment-lerper.d.ts +2 -0
- package/dist/visualization/alignment-lerper.js +68 -0
- package/dist/visualization/camera-follower.d.ts +2 -0
- package/dist/visualization/camera-follower.js +51 -0
- package/dist/visualization/css3d-renderer-manager.d.ts +2 -0
- package/dist/visualization/css3d-renderer-manager.js +41 -0
- package/dist/visualization/gps-compass-cubes.d.ts +2 -0
- package/dist/visualization/gps-compass-cubes.js +129 -0
- package/dist/visualization/gps-event-markers.d.ts +2 -0
- package/dist/visualization/gps-event-markers.js +159 -0
- package/dist/visualization/index.d.ts +12 -0
- package/dist/visualization/index.js +12 -0
- package/dist/visualization/leaflet-map-overlay.d.ts +2 -0
- package/dist/visualization/leaflet-map-overlay.js +313 -0
- package/dist/visualization/lerp-utils.d.ts +2 -0
- package/dist/visualization/lerp-utils.js +22 -0
- package/dist/visualization/map-overlay.d.ts +2 -0
- package/dist/visualization/map-overlay.js +241 -0
- package/dist/visualization/reference-points.d.ts +2 -0
- package/dist/visualization/reference-points.js +160 -0
- package/dist/visualization/three-dispose.d.ts +2 -0
- package/dist/visualization/three-dispose.js +55 -0
- package/dist/visualization/vis-colors.d.ts +2 -0
- package/dist/visualization/vis-colors.js +74 -0
- package/dist/webxr-session-COlSrXdL.d.ts +316 -0
- package/dist/xr-camera-texture-BbukNE79.d.ts +53 -0
- package/dist/xr-error-handler-DTHfmvrl.d.ts +24 -0
- package/dist/zip-export-Rtpi75JJ.d.ts +87 -0
- package/dist/zip-reader-D7pkpX8K.d.ts +97 -0
- package/package.json +104 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import { vi } from "vitest";
|
|
2
|
+
//#region ../src/test-utils/browser-mocks.ts
|
|
3
|
+
/**
|
|
4
|
+
* Browser API Mocks for Testing
|
|
5
|
+
*
|
|
6
|
+
* Provides mock implementations for browser APIs that aren't available in Node.js:
|
|
7
|
+
* - WebXR (navigator.xr, XRSession, XRFrame, XRViewerPose)
|
|
8
|
+
* - Geolocation (navigator.geolocation)
|
|
9
|
+
* - Device Orientation (DeviceOrientationEvent)
|
|
10
|
+
* - File System Access API (showDirectoryPicker, FileSystemDirectoryHandle)
|
|
11
|
+
*
|
|
12
|
+
* @see browser-mocks.md for usage examples
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Create a mock XRViewerPose with specified position and orientation.
|
|
16
|
+
*/
|
|
17
|
+
function createMockPose(position, orientation) {
|
|
18
|
+
const viewTransform = {
|
|
19
|
+
position: {
|
|
20
|
+
...position,
|
|
21
|
+
w: 1
|
|
22
|
+
},
|
|
23
|
+
orientation: { ...orientation }
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
views: [{ transform: viewTransform }],
|
|
27
|
+
transform: {
|
|
28
|
+
matrix: new Float32Array(16),
|
|
29
|
+
position: viewTransform.position,
|
|
30
|
+
orientation: viewTransform.orientation,
|
|
31
|
+
inverse: null
|
|
32
|
+
},
|
|
33
|
+
emulatedPosition: false
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a mock XRFrame that returns the specified pose.
|
|
38
|
+
*/
|
|
39
|
+
function createMockFrame(pose) {
|
|
40
|
+
return {
|
|
41
|
+
getViewerPose: vi.fn(() => pose),
|
|
42
|
+
getDepthInformation: vi.fn(() => null)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Create a mock depth info object with configurable depth function.
|
|
47
|
+
*/
|
|
48
|
+
function createMockDepthInfo(depthFn = () => 1.5) {
|
|
49
|
+
return {
|
|
50
|
+
width: 256,
|
|
51
|
+
height: 256,
|
|
52
|
+
getDepthInMeters: depthFn
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a mock XRSession.
|
|
57
|
+
*/
|
|
58
|
+
function createMockXRSession() {
|
|
59
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
60
|
+
return {
|
|
61
|
+
addEventListener: vi.fn((type, listener) => {
|
|
62
|
+
if (!listeners.has(type)) listeners.set(type, /* @__PURE__ */ new Set());
|
|
63
|
+
listeners.get(type).add(listener);
|
|
64
|
+
}),
|
|
65
|
+
removeEventListener: vi.fn((type, listener) => {
|
|
66
|
+
listeners.get(type)?.delete(listener);
|
|
67
|
+
}),
|
|
68
|
+
end: vi.fn(() => Promise.resolve()),
|
|
69
|
+
requestReferenceSpace: vi.fn(() => Promise.resolve({}))
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create a mock navigator.xr object.
|
|
74
|
+
*/
|
|
75
|
+
function createMockXR(supported = true) {
|
|
76
|
+
const session = createMockXRSession();
|
|
77
|
+
return {
|
|
78
|
+
isSessionSupported: vi.fn(() => Promise.resolve(supported)),
|
|
79
|
+
requestSession: vi.fn(() => Promise.resolve(session))
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Install WebXR mocks on the global navigator object.
|
|
84
|
+
* Returns cleanup function.
|
|
85
|
+
*/
|
|
86
|
+
function installWebXRMocks(supported = true) {
|
|
87
|
+
const mockXR = createMockXR(supported);
|
|
88
|
+
const existingNavigator = typeof navigator !== "undefined" ? navigator : {};
|
|
89
|
+
vi.stubGlobal("navigator", {
|
|
90
|
+
...existingNavigator,
|
|
91
|
+
xr: mockXR
|
|
92
|
+
});
|
|
93
|
+
return () => vi.unstubAllGlobals();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create a mock geolocation position.
|
|
97
|
+
*/
|
|
98
|
+
function createMockGeoPosition(lat, lon, altitude = null, accuracy = 10) {
|
|
99
|
+
const coordsData = {
|
|
100
|
+
latitude: lat,
|
|
101
|
+
longitude: lon,
|
|
102
|
+
altitude,
|
|
103
|
+
accuracy,
|
|
104
|
+
altitudeAccuracy: altitude !== null ? 5 : null,
|
|
105
|
+
heading: null,
|
|
106
|
+
speed: null
|
|
107
|
+
};
|
|
108
|
+
const coords = {
|
|
109
|
+
...coordsData,
|
|
110
|
+
toJSON: () => ({ ...coordsData })
|
|
111
|
+
};
|
|
112
|
+
const timestamp = Date.now();
|
|
113
|
+
return {
|
|
114
|
+
coords,
|
|
115
|
+
timestamp,
|
|
116
|
+
toJSON: () => ({
|
|
117
|
+
coords: { ...coordsData },
|
|
118
|
+
timestamp
|
|
119
|
+
})
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Create a mock geolocation object.
|
|
124
|
+
*/
|
|
125
|
+
function createMockGeolocation() {
|
|
126
|
+
let watchId = 0;
|
|
127
|
+
const watchers = /* @__PURE__ */ new Map();
|
|
128
|
+
const mock = {
|
|
129
|
+
watchPosition: vi.fn((success, _error, _options) => {
|
|
130
|
+
const id = ++watchId;
|
|
131
|
+
watchers.set(id, success);
|
|
132
|
+
return id;
|
|
133
|
+
}),
|
|
134
|
+
clearWatch: vi.fn((id) => {
|
|
135
|
+
watchers.delete(id);
|
|
136
|
+
}),
|
|
137
|
+
getCurrentPosition: vi.fn((success, _error, _options) => {
|
|
138
|
+
success(createMockGeoPosition(50, 8));
|
|
139
|
+
})
|
|
140
|
+
};
|
|
141
|
+
const mockWithHelpers = mock;
|
|
142
|
+
mockWithHelpers.simulatePosition = (pos) => {
|
|
143
|
+
for (const callback of watchers.values()) callback(pos);
|
|
144
|
+
};
|
|
145
|
+
return mock;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Install geolocation mocks on the global navigator object.
|
|
149
|
+
* Returns cleanup function.
|
|
150
|
+
*/
|
|
151
|
+
function installGeolocationMocks() {
|
|
152
|
+
const mockGeo = createMockGeolocation();
|
|
153
|
+
const existingNavigator = typeof navigator !== "undefined" ? navigator : {};
|
|
154
|
+
vi.stubGlobal("navigator", {
|
|
155
|
+
...existingNavigator,
|
|
156
|
+
geolocation: mockGeo
|
|
157
|
+
});
|
|
158
|
+
return () => vi.unstubAllGlobals();
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a mock DeviceOrientationEvent.
|
|
162
|
+
*/
|
|
163
|
+
function createMockOrientationEvent(alpha, beta, gamma, absolute = false) {
|
|
164
|
+
return {
|
|
165
|
+
alpha,
|
|
166
|
+
beta,
|
|
167
|
+
gamma,
|
|
168
|
+
absolute
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Create a mock DeviceOrientationEvent class with requestPermission support.
|
|
173
|
+
*/
|
|
174
|
+
function createMockDeviceOrientationEventClass(permissionGranted = true) {
|
|
175
|
+
return class MockDeviceOrientationEvent {
|
|
176
|
+
alpha;
|
|
177
|
+
beta;
|
|
178
|
+
gamma;
|
|
179
|
+
absolute;
|
|
180
|
+
constructor(type, init) {
|
|
181
|
+
this.alpha = init?.alpha ?? null;
|
|
182
|
+
this.beta = init?.beta ?? null;
|
|
183
|
+
this.gamma = init?.gamma ?? null;
|
|
184
|
+
this.absolute = init?.absolute ?? false;
|
|
185
|
+
}
|
|
186
|
+
static requestPermission = vi.fn(() => Promise.resolve(permissionGranted ? "granted" : "denied"));
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Create a mock file handle.
|
|
191
|
+
*/
|
|
192
|
+
function createMockFileHandle(name, content = "") {
|
|
193
|
+
const file = new File([content], name, { type: "application/json" });
|
|
194
|
+
const writable = {
|
|
195
|
+
write: vi.fn(() => Promise.resolve()),
|
|
196
|
+
close: vi.fn(() => Promise.resolve())
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
kind: "file",
|
|
200
|
+
name,
|
|
201
|
+
getFile: vi.fn(() => Promise.resolve(file)),
|
|
202
|
+
createWritable: vi.fn(() => Promise.resolve(writable))
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Create a mock directory handle with specified files.
|
|
207
|
+
*/
|
|
208
|
+
function createMockDirectoryHandle(name, files = /* @__PURE__ */ new Map(), subdirs = /* @__PURE__ */ new Map()) {
|
|
209
|
+
return {
|
|
210
|
+
kind: "directory",
|
|
211
|
+
name,
|
|
212
|
+
entries: vi.fn(function* () {
|
|
213
|
+
for (const [fileName, content] of files) yield [fileName, createMockFileHandle(fileName, content)];
|
|
214
|
+
for (const [dirName, dirHandle] of subdirs) yield [dirName, dirHandle];
|
|
215
|
+
}),
|
|
216
|
+
getFileHandle: vi.fn((fileName, options) => {
|
|
217
|
+
if (files.has(fileName)) return Promise.resolve(createMockFileHandle(fileName, files.get(fileName)));
|
|
218
|
+
if (options?.create) {
|
|
219
|
+
files.set(fileName, "");
|
|
220
|
+
return Promise.resolve(createMockFileHandle(fileName, ""));
|
|
221
|
+
}
|
|
222
|
+
return Promise.reject(new DOMException(`File ${fileName} not found`, "NotFoundError"));
|
|
223
|
+
}),
|
|
224
|
+
getDirectoryHandle: vi.fn((dirName, options) => {
|
|
225
|
+
if (subdirs.has(dirName)) return Promise.resolve(subdirs.get(dirName));
|
|
226
|
+
if (options?.create) {
|
|
227
|
+
const newDir = createMockDirectoryHandle(dirName);
|
|
228
|
+
subdirs.set(dirName, newDir);
|
|
229
|
+
return Promise.resolve(newDir);
|
|
230
|
+
}
|
|
231
|
+
return Promise.reject(new DOMException(`Directory ${dirName} not found`, "NotFoundError"));
|
|
232
|
+
}),
|
|
233
|
+
removeEntry: vi.fn((entryName) => {
|
|
234
|
+
files.delete(entryName);
|
|
235
|
+
subdirs.delete(entryName);
|
|
236
|
+
return Promise.resolve();
|
|
237
|
+
})
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Class-based mock FileSystemFileHandle for tests requiring mutable state.
|
|
242
|
+
* Use this when you need to modify file contents during tests.
|
|
243
|
+
*/
|
|
244
|
+
var MockFSFileHandle = class {
|
|
245
|
+
kind = "file";
|
|
246
|
+
name;
|
|
247
|
+
content;
|
|
248
|
+
constructor(name, content = "") {
|
|
249
|
+
this.name = name;
|
|
250
|
+
this.content = content;
|
|
251
|
+
}
|
|
252
|
+
getFile() {
|
|
253
|
+
const type = this.name.endsWith(".zip") ? "application/zip" : "application/json";
|
|
254
|
+
const blobPart = typeof this.content === "string" ? this.content : this.content.buffer;
|
|
255
|
+
return Promise.resolve(new File([blobPart], this.name, { type }));
|
|
256
|
+
}
|
|
257
|
+
createWritable() {
|
|
258
|
+
const writable = {
|
|
259
|
+
write: vi.fn((data) => {
|
|
260
|
+
this.content = data;
|
|
261
|
+
return Promise.resolve();
|
|
262
|
+
}),
|
|
263
|
+
close: vi.fn(() => Promise.resolve())
|
|
264
|
+
};
|
|
265
|
+
return Promise.resolve(writable);
|
|
266
|
+
}
|
|
267
|
+
createSyncAccessHandle() {
|
|
268
|
+
throw new Error("Not implemented");
|
|
269
|
+
}
|
|
270
|
+
isSameEntry() {
|
|
271
|
+
throw new Error("Not implemented");
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
/**
|
|
275
|
+
* Class-based mock FileSystemDirectoryHandle for tests requiring addFile/addDirectory.
|
|
276
|
+
* Implements the full FileSystemDirectoryHandle interface.
|
|
277
|
+
*/
|
|
278
|
+
var MockFSDirectoryHandle = class MockFSDirectoryHandle {
|
|
279
|
+
kind = "directory";
|
|
280
|
+
name;
|
|
281
|
+
contents = /* @__PURE__ */ new Map();
|
|
282
|
+
constructor(name) {
|
|
283
|
+
this.name = name;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Add a file to this directory.
|
|
287
|
+
* Accepts string content (for JSON etc) or Uint8Array (for zip bytes).
|
|
288
|
+
*/
|
|
289
|
+
addFile(name, content) {
|
|
290
|
+
this.contents.set(name, new MockFSFileHandle(name, content));
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Add a subdirectory to this directory.
|
|
294
|
+
*/
|
|
295
|
+
addDirectory(name, handle) {
|
|
296
|
+
this.contents.set(name, handle);
|
|
297
|
+
}
|
|
298
|
+
async *entries() {
|
|
299
|
+
await Promise.resolve();
|
|
300
|
+
for (const entry of this.contents) yield entry;
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
async *[Symbol.asyncIterator]() {
|
|
304
|
+
await Promise.resolve();
|
|
305
|
+
for (const entry of this.contents) yield entry;
|
|
306
|
+
return void 0;
|
|
307
|
+
}
|
|
308
|
+
getDirectoryHandle(name, options) {
|
|
309
|
+
const handle = this.contents.get(name);
|
|
310
|
+
if (handle && handle.kind === "directory") return Promise.resolve(handle);
|
|
311
|
+
if (options?.create) {
|
|
312
|
+
const newHandle = new MockFSDirectoryHandle(name);
|
|
313
|
+
this.contents.set(name, newHandle);
|
|
314
|
+
return Promise.resolve(newHandle);
|
|
315
|
+
}
|
|
316
|
+
return Promise.reject(new DOMException(`Directory ${name} not found`, "NotFoundError"));
|
|
317
|
+
}
|
|
318
|
+
getFileHandle(name, options) {
|
|
319
|
+
const handle = this.contents.get(name);
|
|
320
|
+
if (handle && handle.kind === "file") return Promise.resolve(handle);
|
|
321
|
+
if (options?.create) {
|
|
322
|
+
const newHandle = new MockFSFileHandle(name, "");
|
|
323
|
+
this.contents.set(name, newHandle);
|
|
324
|
+
return Promise.resolve(newHandle);
|
|
325
|
+
}
|
|
326
|
+
return Promise.reject(new DOMException(`File ${name} not found`, "NotFoundError"));
|
|
327
|
+
}
|
|
328
|
+
removeEntry(name) {
|
|
329
|
+
this.contents.delete(name);
|
|
330
|
+
return Promise.resolve();
|
|
331
|
+
}
|
|
332
|
+
resolve() {
|
|
333
|
+
throw new Error("Not implemented");
|
|
334
|
+
}
|
|
335
|
+
isSameEntry() {
|
|
336
|
+
throw new Error("Not implemented");
|
|
337
|
+
}
|
|
338
|
+
async *values() {
|
|
339
|
+
await Promise.resolve();
|
|
340
|
+
for (const handle of this.contents.values()) yield handle;
|
|
341
|
+
return void 0;
|
|
342
|
+
}
|
|
343
|
+
async *keys() {
|
|
344
|
+
await Promise.resolve();
|
|
345
|
+
for (const name of this.contents.keys()) yield name;
|
|
346
|
+
return void 0;
|
|
347
|
+
}
|
|
348
|
+
queryPermission(_descriptor) {
|
|
349
|
+
return Promise.resolve("granted");
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* Install File System Access API mocks on the global window object.
|
|
354
|
+
* Returns cleanup function.
|
|
355
|
+
*/
|
|
356
|
+
function installFileSystemMocks(rootDir) {
|
|
357
|
+
const mockRoot = rootDir ?? createMockDirectoryHandle("root");
|
|
358
|
+
const showDirectoryPicker = vi.fn(() => Promise.resolve(mockRoot));
|
|
359
|
+
vi.stubGlobal("showDirectoryPicker", showDirectoryPicker);
|
|
360
|
+
return () => vi.unstubAllGlobals();
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Install all browser mocks at once.
|
|
364
|
+
* Returns cleanup function.
|
|
365
|
+
*/
|
|
366
|
+
function installAllBrowserMocks(options) {
|
|
367
|
+
const cleanups = [];
|
|
368
|
+
if (options?.webxrSupported !== false) cleanups.push(installWebXRMocks(options?.webxrSupported ?? true));
|
|
369
|
+
if (options?.geolocation !== false) cleanups.push(installGeolocationMocks());
|
|
370
|
+
if (options?.fileSystem !== false) cleanups.push(installFileSystemMocks(options?.rootDir));
|
|
371
|
+
return () => {
|
|
372
|
+
for (const cleanup of cleanups) cleanup();
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* In-memory file system for OPFS mocking.
|
|
377
|
+
* Stores file contents as ArrayBuffer for binary compatibility.
|
|
378
|
+
*/
|
|
379
|
+
var MockOPFSDirectoryHandle = class MockOPFSDirectoryHandle {
|
|
380
|
+
kind = "directory";
|
|
381
|
+
name;
|
|
382
|
+
contents = /* @__PURE__ */ new Map();
|
|
383
|
+
fileContents = /* @__PURE__ */ new Map();
|
|
384
|
+
constructor(name) {
|
|
385
|
+
this.name = name;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Get stored file content (for test assertions).
|
|
389
|
+
*/
|
|
390
|
+
getStoredContent(fileName) {
|
|
391
|
+
return this.fileContents.get(fileName);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Get stored file content as string (for test assertions).
|
|
395
|
+
*/
|
|
396
|
+
getStoredContentAsString(fileName) {
|
|
397
|
+
const content = this.fileContents.get(fileName);
|
|
398
|
+
if (!content) return;
|
|
399
|
+
return new TextDecoder().decode(content);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Set file content directly (for test setup).
|
|
403
|
+
* Also ensures a corresponding file handle exists in contents map
|
|
404
|
+
* so that getFileHandle() and iterators work correctly.
|
|
405
|
+
*/
|
|
406
|
+
setStoredContent(fileName, content) {
|
|
407
|
+
if (typeof content === "string") this.fileContents.set(fileName, new TextEncoder().encode(content).buffer);
|
|
408
|
+
else this.fileContents.set(fileName, content);
|
|
409
|
+
if (!this.contents.has(fileName)) this.contents.set(fileName, new MockOPFSFileHandle(fileName, this, fileName));
|
|
410
|
+
}
|
|
411
|
+
async *entries() {
|
|
412
|
+
await Promise.resolve();
|
|
413
|
+
for (const entry of this.contents) yield entry;
|
|
414
|
+
return void 0;
|
|
415
|
+
}
|
|
416
|
+
async *[Symbol.asyncIterator]() {
|
|
417
|
+
await Promise.resolve();
|
|
418
|
+
for (const entry of this.contents) yield entry;
|
|
419
|
+
return void 0;
|
|
420
|
+
}
|
|
421
|
+
getDirectoryHandle(name, options) {
|
|
422
|
+
const handle = this.contents.get(name);
|
|
423
|
+
if (handle && handle.kind === "directory") return Promise.resolve(handle);
|
|
424
|
+
if (options?.create) {
|
|
425
|
+
const newHandle = new MockOPFSDirectoryHandle(name);
|
|
426
|
+
this.contents.set(name, newHandle);
|
|
427
|
+
return Promise.resolve(newHandle);
|
|
428
|
+
}
|
|
429
|
+
return Promise.reject(new DOMException(`Directory ${name} not found`, "NotFoundError"));
|
|
430
|
+
}
|
|
431
|
+
getFileHandle(name, options) {
|
|
432
|
+
const handle = this.contents.get(name);
|
|
433
|
+
if (handle && handle.kind === "file") return Promise.resolve(handle);
|
|
434
|
+
if (options?.create) {
|
|
435
|
+
const newHandle = new MockOPFSFileHandle(name, this, name);
|
|
436
|
+
this.contents.set(name, newHandle);
|
|
437
|
+
return Promise.resolve(newHandle);
|
|
438
|
+
}
|
|
439
|
+
return Promise.reject(new DOMException(`File ${name} not found`, "NotFoundError"));
|
|
440
|
+
}
|
|
441
|
+
removeEntry(name) {
|
|
442
|
+
this.contents.delete(name);
|
|
443
|
+
this.fileContents.delete(name);
|
|
444
|
+
return Promise.resolve();
|
|
445
|
+
}
|
|
446
|
+
resolve() {
|
|
447
|
+
throw new Error("Not implemented");
|
|
448
|
+
}
|
|
449
|
+
isSameEntry() {
|
|
450
|
+
throw new Error("Not implemented");
|
|
451
|
+
}
|
|
452
|
+
async *values() {
|
|
453
|
+
await Promise.resolve();
|
|
454
|
+
for (const handle of this.contents.values()) yield handle;
|
|
455
|
+
return void 0;
|
|
456
|
+
}
|
|
457
|
+
async *keys() {
|
|
458
|
+
await Promise.resolve();
|
|
459
|
+
for (const name of this.contents.keys()) yield name;
|
|
460
|
+
return void 0;
|
|
461
|
+
}
|
|
462
|
+
queryPermission(_descriptor) {
|
|
463
|
+
return Promise.resolve("granted");
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
/**
|
|
467
|
+
* Mock OPFS file handle with writable stream support.
|
|
468
|
+
*/
|
|
469
|
+
var MockOPFSFileHandle = class {
|
|
470
|
+
kind = "file";
|
|
471
|
+
name;
|
|
472
|
+
parentDir;
|
|
473
|
+
storageKey;
|
|
474
|
+
constructor(name, parentDir, storageKey) {
|
|
475
|
+
this.name = name;
|
|
476
|
+
this.parentDir = parentDir;
|
|
477
|
+
this.storageKey = storageKey;
|
|
478
|
+
}
|
|
479
|
+
getFile() {
|
|
480
|
+
const content = this.parentDir.getStoredContent(this.storageKey);
|
|
481
|
+
const contentArray = content ? new Uint8Array(content) : new Uint8Array(0);
|
|
482
|
+
const file = new File([contentArray], this.name, { type: "application/octet-stream" });
|
|
483
|
+
if (typeof file.text !== "function") file.text = () => Promise.resolve(new TextDecoder().decode(contentArray));
|
|
484
|
+
if (typeof file.arrayBuffer !== "function") file.arrayBuffer = () => Promise.resolve(contentArray.buffer.slice(0));
|
|
485
|
+
return Promise.resolve(file);
|
|
486
|
+
}
|
|
487
|
+
createWritable() {
|
|
488
|
+
const chunks = [];
|
|
489
|
+
const parentDir = this.parentDir;
|
|
490
|
+
const storageKey = this.storageKey;
|
|
491
|
+
const writable = {
|
|
492
|
+
write: vi.fn(async (data) => {
|
|
493
|
+
if (typeof data === "string") chunks.push(new TextEncoder().encode(data).buffer);
|
|
494
|
+
else if (data instanceof Blob) if (typeof data.arrayBuffer === "function") {
|
|
495
|
+
const buffer = await data.arrayBuffer();
|
|
496
|
+
chunks.push(buffer);
|
|
497
|
+
} else {
|
|
498
|
+
const text = await new Promise((resolve, reject) => {
|
|
499
|
+
const reader = new FileReader();
|
|
500
|
+
reader.onload = () => resolve(reader.result);
|
|
501
|
+
reader.onerror = reject;
|
|
502
|
+
reader.readAsText(data);
|
|
503
|
+
});
|
|
504
|
+
chunks.push(new TextEncoder().encode(text).buffer);
|
|
505
|
+
}
|
|
506
|
+
else if (data instanceof ArrayBuffer) chunks.push(data);
|
|
507
|
+
else if (ArrayBuffer.isView(data)) chunks.push(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
|
|
508
|
+
}),
|
|
509
|
+
close: vi.fn(() => {
|
|
510
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
511
|
+
const combined = new Uint8Array(totalLength);
|
|
512
|
+
let offset = 0;
|
|
513
|
+
for (const chunk of chunks) {
|
|
514
|
+
combined.set(new Uint8Array(chunk), offset);
|
|
515
|
+
offset += chunk.byteLength;
|
|
516
|
+
}
|
|
517
|
+
parentDir.setStoredContent(storageKey, combined.buffer);
|
|
518
|
+
return Promise.resolve();
|
|
519
|
+
}),
|
|
520
|
+
seek: vi.fn(),
|
|
521
|
+
truncate: vi.fn(),
|
|
522
|
+
abort: vi.fn(() => Promise.resolve()),
|
|
523
|
+
locked: false,
|
|
524
|
+
getWriter: vi.fn()
|
|
525
|
+
};
|
|
526
|
+
return Promise.resolve(writable);
|
|
527
|
+
}
|
|
528
|
+
createSyncAccessHandle() {
|
|
529
|
+
throw new Error("Not implemented in mock");
|
|
530
|
+
}
|
|
531
|
+
isSameEntry() {
|
|
532
|
+
throw new Error("Not implemented");
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
/**
|
|
536
|
+
* Create a mock StorageManager with OPFS support.
|
|
537
|
+
*/
|
|
538
|
+
function createMockStorageManager(opfsRoot) {
|
|
539
|
+
const root = opfsRoot ?? new MockOPFSDirectoryHandle("");
|
|
540
|
+
return {
|
|
541
|
+
getDirectory: vi.fn(() => Promise.resolve(root)),
|
|
542
|
+
estimate: vi.fn(() => Promise.resolve({
|
|
543
|
+
quota: 1024 * 1024 * 1024,
|
|
544
|
+
usage: 0
|
|
545
|
+
})),
|
|
546
|
+
persist: vi.fn(() => Promise.resolve(true)),
|
|
547
|
+
persisted: vi.fn(() => Promise.resolve(true))
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Install OPFS mocks on the global navigator object.
|
|
552
|
+
* Returns the mock OPFS root directory for test assertions and cleanup function.
|
|
553
|
+
*/
|
|
554
|
+
function installOPFSMocks(opfsRoot) {
|
|
555
|
+
const root = opfsRoot ?? new MockOPFSDirectoryHandle("");
|
|
556
|
+
const mockStorage = createMockStorageManager(root);
|
|
557
|
+
const existingNavigator = typeof navigator !== "undefined" ? navigator : {};
|
|
558
|
+
vi.stubGlobal("navigator", {
|
|
559
|
+
...existingNavigator,
|
|
560
|
+
storage: mockStorage
|
|
561
|
+
});
|
|
562
|
+
return {
|
|
563
|
+
root,
|
|
564
|
+
cleanup: () => vi.unstubAllGlobals()
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
//#endregion
|
|
568
|
+
export { MockFSDirectoryHandle, MockFSFileHandle, MockOPFSDirectoryHandle, MockOPFSFileHandle, createMockDepthInfo, createMockDeviceOrientationEventClass, createMockDirectoryHandle, createMockFileHandle, createMockFrame, createMockGeoPosition, createMockGeolocation, createMockOrientationEvent, createMockPose, createMockStorageManager, createMockXR, createMockXRSession, installAllBrowserMocks, installFileSystemMocks, installGeolocationMocks, installOPFSMocks, installWebXRMocks };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { i as RecordedAction } from "../zip-reader-D7pkpX8K.js";
|
|
2
|
+
import { LatLong } from "gps-plus-slam-js";
|
|
3
|
+
|
|
4
|
+
//#region ../src/test-utils/zip-round-trip-helpers.d.ts
|
|
5
|
+
/** Configuration for producing a test zip. All fields have sensible defaults. */
|
|
6
|
+
interface ProduceTestZipOptions {
|
|
7
|
+
scenarioName: string;
|
|
8
|
+
sessionTimestamp: Date;
|
|
9
|
+
gpsEventCount: number;
|
|
10
|
+
imagesBeforeSetZero: number;
|
|
11
|
+
imagesAfterSetZero: number;
|
|
12
|
+
frameCount: number;
|
|
13
|
+
deviceInfo: string;
|
|
14
|
+
zeroPos: LatLong;
|
|
15
|
+
}
|
|
16
|
+
/** Result of producing a test zip, with all metadata needed for assertions. */
|
|
17
|
+
interface TestZipResult {
|
|
18
|
+
/** The zip file as a Uint8Array, ready for loadActionsFromZip / readZipEntries */
|
|
19
|
+
zipData: Uint8Array;
|
|
20
|
+
/** Total number of Redux action files in the zip */
|
|
21
|
+
totalActionCount: number;
|
|
22
|
+
/** Number of gpsData/recordGpsEvent actions */
|
|
23
|
+
gpsEventCount: number;
|
|
24
|
+
/** Breakdown of gpsData/add2dImage actions */
|
|
25
|
+
imageActions: {
|
|
26
|
+
totalCount: number;
|
|
27
|
+
beforeSetZero: number;
|
|
28
|
+
afterSetZero: number;
|
|
29
|
+
};
|
|
30
|
+
scenarioName: string;
|
|
31
|
+
sessionName: string;
|
|
32
|
+
startTime: number;
|
|
33
|
+
deviceInfo: string;
|
|
34
|
+
/** Whether session.json is present in the zip */
|
|
35
|
+
hasSessionJson: boolean;
|
|
36
|
+
/** The zero reference position used for GPS events */
|
|
37
|
+
zeroPos: LatLong;
|
|
38
|
+
/** All actions as written, in order, for assertion comparisons */
|
|
39
|
+
actions: RecordedAction[];
|
|
40
|
+
/** Number of frame files in the zip */
|
|
41
|
+
frameCount: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Produce a realistic recording zip using the real OPFS mock + export pipeline.
|
|
45
|
+
*
|
|
46
|
+
* Action sequence produced (with defaults):
|
|
47
|
+
* 1. recorder/startSession (index 1)
|
|
48
|
+
* 2. gpsData/add2dImage × 2 (indices 2-3, before setZeroPos — dropped on replay)
|
|
49
|
+
* 3. gpsData/setZeroPos (index 4)
|
|
50
|
+
* 4. gpsData/recordGpsEvent × 10 (indices 5-14)
|
|
51
|
+
* 5. gpsData/add2dImage × 5 (indices 15-19, after setZeroPos — kept on replay)
|
|
52
|
+
*
|
|
53
|
+
* GPS events use varied odom/GPS positions that produce a non-identity
|
|
54
|
+
* alignment matrix when replayed through the library store.
|
|
55
|
+
*
|
|
56
|
+
* OPFS mocks are installed and cleaned up within this function.
|
|
57
|
+
*/
|
|
58
|
+
declare function produceTestZip(opts?: Partial<ProduceTestZipOptions>): Promise<TestZipResult>;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { ProduceTestZipOptions, type RecordedAction, TestZipResult, produceTestZip };
|