@avimate/msfs-jest-utils 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 ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ivan Slepchenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # MSFS Unit Test Framework
2
+
3
+ Unit testing framework for MSFS HTML/JS instruments (Jest + JSDOM).
4
+
5
+ Фреймворк для unit-тестирования MSFS инструментов с поддержкой DOM через jsdom.
6
+
7
+ ## Особенности
8
+
9
+ - ✅ **Полная поддержка DOM** - использует jsdom для рендеринга компонентов
10
+ - ✅ **FSComponent.render()** - можно рендерить компоненты и тестировать DOM элементы
11
+ - ✅ **Моки SimVar и Coherent** - полная эмуляция API симулятора
12
+ - ✅ **Jest интеграция** - готовые утилиты для тестирования
13
+ - ✅ **TypeScript поддержка** - полная типизация
14
+
15
+ ## Установка
16
+
17
+ ### As a library (recommended)
18
+
19
+ ```bash
20
+ npm install --save-dev @avimate/msfs-jest-utils
21
+ ```
22
+
23
+ ### Local development
24
+
25
+ ```bash
26
+ npm install
27
+ npm run build
28
+ ```
29
+
30
+ ## Использование
31
+
32
+ ### Базовый пример
33
+
34
+ ```typescript
35
+ import { TestEnvironment, ComponentTestHelper } from './msfs-unit-test-framework';
36
+ import { MyComponent } from '../html_ui/MyComponent';
37
+
38
+ describe('MyComponent', () => {
39
+ let env: TestEnvironment;
40
+ let helper: ComponentTestHelper;
41
+
42
+ beforeEach(() => {
43
+ env = new TestEnvironment();
44
+ env.setup();
45
+ helper = new ComponentTestHelper(env);
46
+ });
47
+
48
+ afterEach(() => {
49
+ helper.cleanup();
50
+ env.teardown();
51
+ });
52
+
53
+ test('should render component', () => {
54
+ const { element } = helper.renderComponent(MyComponent, {
55
+ value: 42
56
+ });
57
+
58
+ expect(element).toBeTruthy();
59
+ expect(element.textContent).toContain('42');
60
+ });
61
+
62
+ test('should update when SimVar changes', () => {
63
+ env.setSimVar('L:MY_VALUE', 'number', 10);
64
+
65
+ const { element } = helper.renderComponent(MyComponent, {
66
+ valueSource: env.getSubject('L:MY_VALUE', 'number')
67
+ });
68
+
69
+ expect(helper.getTextContent('.value')).toBe('10');
70
+
71
+ env.setSimVar('L:MY_VALUE', 'number', 20);
72
+ // Wait for subscription update
73
+ await helper.waitForUpdate(50);
74
+
75
+ expect(helper.getTextContent('.value')).toBe('20');
76
+ });
77
+ });
78
+ ```
79
+
80
+ ### Тестирование DOM элементов
81
+
82
+ ```typescript
83
+ test('should have correct CSS classes', () => {
84
+ const { element } = helper.renderComponent(MyComponent, {});
85
+
86
+ expect(helper.hasClass('.my-component', 'active')).toBe(true);
87
+ expect(helper.getAttribute('.my-component', 'data-state')).toBe('ready');
88
+ });
89
+
90
+ test('should apply styles correctly', () => {
91
+ const { element } = helper.renderComponent(MyComponent, {
92
+ width: 100,
93
+ height: 200
94
+ });
95
+
96
+ expect(helper.getStyle('.my-component', 'width')).toBe('100px');
97
+ });
98
+ ```
99
+
100
+ ### Работа с SimVar
101
+
102
+ ```typescript
103
+ test('should read SimVar values', () => {
104
+ env.setSimVar('L:TEST_VALUE', 'number', 42);
105
+
106
+ const value = env.getSimVar('L:TEST_VALUE', 'number');
107
+ expect(value).toBe(42);
108
+ });
109
+
110
+ test('should track SimVar access', () => {
111
+ env.setSimVar('L:TEST', 'number', 10);
112
+ SimVar.GetSimVarValue('L:TEST', 'number');
113
+
114
+ const log = env.getSimVarAccessLog();
115
+ expect(log).toHaveLength(1);
116
+ expect(log[0].operation).toBe('get');
117
+ });
118
+ ```
119
+
120
+ ## API
121
+
122
+ ### TestEnvironment
123
+
124
+ - `setup()` - настройка тестового окружения
125
+ - `teardown()` - очистка после тестов
126
+ - `reset()` - сброс моков
127
+ - `setSimVar(name, unit, value)` - установить SimVar
128
+ - `getSimVar(name, unit)` - получить SimVar
129
+ - `getDocument()` - получить jsdom document
130
+ - `getWindow()` - получить jsdom window
131
+
132
+ ### ComponentTestHelper
133
+
134
+ - `renderComponent(ComponentClass, props)` - рендерить компонент
135
+ - `querySelector(selector)` - найти элемент
136
+ - `getTextContent(selector)` - получить текст
137
+ - `hasClass(selector, className)` - проверить класс
138
+ - `getAttribute(selector, attrName)` - получить атрибут
139
+ - `getStyle(selector, property)` - получить стиль
140
+ - `waitForUpdate(ms)` - подождать обновления
141
+ - `cleanup()` - очистка
142
+
143
+ ## Структура
144
+
145
+ ```
146
+ msfs-unit-test-framework/
147
+ ├── src/
148
+ │ ├── mocks/
149
+ │ │ ├── SimVarMock.ts # Мок SimVar API
150
+ │ │ ├── CoherentMock.ts # Мок Coherent API
151
+ │ │ └── index.ts
152
+ │ ├── test-utils/
153
+ │ │ ├── TestEnvironment.ts # Настройка окружения
154
+ │ │ ├── ComponentTestHelper.ts # Утилиты для компонентов
155
+ │ │ └── index.ts
156
+ │ ├── setupTests.ts # Jest setup
157
+ │ └── index.ts # Главный экспорт
158
+ ├── tests/ # Примеры тестов
159
+ ├── jest.config.js
160
+ ├── tsconfig.json
161
+ └── package.json
162
+ ```
163
+
164
+ ## Примеры
165
+
166
+ Смотрите папку `tests/` для примеров использования.
167
+
168
+
169
+
170
+
@@ -0,0 +1,8 @@
1
+ /**
2
+ * MSFS Unit Test Framework
3
+ *
4
+ * A comprehensive testing framework for MSFS HTML/JS instruments.
5
+ * Supports DOM testing via jsdom and FSComponent.render()
6
+ */
7
+ export * from './mocks';
8
+ export * from './test-utils';
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * MSFS Unit Test Framework
4
+ *
5
+ * A comprehensive testing framework for MSFS HTML/JS instruments.
6
+ * Supports DOM testing via jsdom and FSComponent.render()
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
+ };
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ __exportStar(require("./mocks"), exports);
24
+ __exportStar(require("./test-utils"), exports);
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Mock implementation of Coherent Browser API for testing MSFS instruments.
3
+ *
4
+ * Coherent is the embedded browser used by MSFS. This mock provides:
5
+ * - Coherent.call() for calling simulator functions
6
+ * - Event system simulation
7
+ * - Browser API compatibility
8
+ */
9
+ export interface CoherentCall {
10
+ method: string;
11
+ args: any[];
12
+ timestamp: number;
13
+ resolve?: (value: any) => void;
14
+ reject?: (error: any) => void;
15
+ }
16
+ /**
17
+ * Mock Coherent Browser implementation
18
+ */
19
+ export declare class CoherentMock {
20
+ private static instance;
21
+ private callHistory;
22
+ private eventListeners;
23
+ private callHandlers;
24
+ private maxHistorySize;
25
+ private constructor();
26
+ /**
27
+ * Get singleton instance
28
+ */
29
+ static getInstance(): CoherentMock;
30
+ /**
31
+ * Initialize default call handlers for common MSFS functions
32
+ */
33
+ private initializeDefaultHandlers;
34
+ /**
35
+ * Mock Coherent.call() - calls a function in the simulator
36
+ */
37
+ call(method: string, ...args: any[]): Promise<any>;
38
+ /**
39
+ * Register a handler for a specific Coherent call
40
+ */
41
+ registerHandler(method: string, handler: (...args: any[]) => any): void;
42
+ /**
43
+ * Trigger an event (simulates simulator events)
44
+ */
45
+ triggerEvent(eventName: string, data: any): void;
46
+ /**
47
+ * Add event listener
48
+ */
49
+ on(eventName: string, callback: (data: any) => void): void;
50
+ /**
51
+ * Remove event listener
52
+ */
53
+ off(eventName: string, callback: (data: any) => void): void;
54
+ /**
55
+ * Get call history
56
+ */
57
+ getCallHistory(): CoherentCall[];
58
+ /**
59
+ * Get calls for a specific method
60
+ */
61
+ getCallsForMethod(method: string): CoherentCall[];
62
+ /**
63
+ * Clear call history
64
+ */
65
+ clearHistory(): void;
66
+ /**
67
+ * Reset all handlers and history
68
+ */
69
+ reset(): void;
70
+ }
71
+ /**
72
+ * Global Coherent mock instance
73
+ */
74
+ export declare const coherentMock: CoherentMock;
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * Mock implementation of Coherent Browser API for testing MSFS instruments.
4
+ *
5
+ * Coherent is the embedded browser used by MSFS. This mock provides:
6
+ * - Coherent.call() for calling simulator functions
7
+ * - Event system simulation
8
+ * - Browser API compatibility
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.coherentMock = exports.CoherentMock = void 0;
12
+ /**
13
+ * Mock Coherent Browser implementation
14
+ */
15
+ class CoherentMock {
16
+ constructor() {
17
+ this.callHistory = [];
18
+ this.eventListeners = new Map();
19
+ this.callHandlers = new Map();
20
+ this.maxHistorySize = 10000;
21
+ this.initializeDefaultHandlers();
22
+ }
23
+ /**
24
+ * Get singleton instance
25
+ */
26
+ static getInstance() {
27
+ if (!CoherentMock.instance) {
28
+ CoherentMock.instance = new CoherentMock();
29
+ }
30
+ return CoherentMock.instance;
31
+ }
32
+ /**
33
+ * Initialize default call handlers for common MSFS functions
34
+ */
35
+ initializeDefaultHandlers() {
36
+ // SimVar set handlers - will be connected to SimVarMock in setup
37
+ this.registerHandler('setValueReg_Number', (id, value) => {
38
+ // This will be connected to SimVarMock in TestEnvironment
39
+ return Promise.resolve();
40
+ });
41
+ this.registerHandler('setValueReg_String', (id, value) => {
42
+ return Promise.resolve();
43
+ });
44
+ this.registerHandler('setValueReg_Bool', (id, value) => {
45
+ return Promise.resolve();
46
+ });
47
+ }
48
+ /**
49
+ * Mock Coherent.call() - calls a function in the simulator
50
+ */
51
+ call(method, ...args) {
52
+ const call = {
53
+ method,
54
+ args,
55
+ timestamp: Date.now()
56
+ };
57
+ this.callHistory.push(call);
58
+ // Keep history size manageable
59
+ if (this.callHistory.length > this.maxHistorySize) {
60
+ this.callHistory = this.callHistory.slice(-this.maxHistorySize);
61
+ }
62
+ // Check if we have a registered handler
63
+ const handler = this.callHandlers.get(method);
64
+ if (handler) {
65
+ try {
66
+ const result = handler(...args);
67
+ return Promise.resolve(result);
68
+ }
69
+ catch (error) {
70
+ return Promise.reject(error);
71
+ }
72
+ }
73
+ // Default: return resolved promise
74
+ return Promise.resolve(null);
75
+ }
76
+ /**
77
+ * Register a handler for a specific Coherent call
78
+ */
79
+ registerHandler(method, handler) {
80
+ this.callHandlers.set(method, handler);
81
+ }
82
+ /**
83
+ * Trigger an event (simulates simulator events)
84
+ */
85
+ triggerEvent(eventName, data) {
86
+ const listeners = this.eventListeners.get(eventName);
87
+ if (listeners) {
88
+ listeners.forEach(listener => {
89
+ try {
90
+ listener(data);
91
+ }
92
+ catch (error) {
93
+ console.error(`Error in event listener for ${eventName}:`, error);
94
+ }
95
+ });
96
+ }
97
+ }
98
+ /**
99
+ * Add event listener
100
+ */
101
+ on(eventName, callback) {
102
+ if (!this.eventListeners.has(eventName)) {
103
+ this.eventListeners.set(eventName, new Set());
104
+ }
105
+ this.eventListeners.get(eventName).add(callback);
106
+ }
107
+ /**
108
+ * Remove event listener
109
+ */
110
+ off(eventName, callback) {
111
+ const listeners = this.eventListeners.get(eventName);
112
+ if (listeners) {
113
+ listeners.delete(callback);
114
+ }
115
+ }
116
+ /**
117
+ * Get call history
118
+ */
119
+ getCallHistory() {
120
+ return [...this.callHistory];
121
+ }
122
+ /**
123
+ * Get calls for a specific method
124
+ */
125
+ getCallsForMethod(method) {
126
+ return this.callHistory.filter(call => call.method === method);
127
+ }
128
+ /**
129
+ * Clear call history
130
+ */
131
+ clearHistory() {
132
+ this.callHistory = [];
133
+ }
134
+ /**
135
+ * Reset all handlers and history
136
+ */
137
+ reset() {
138
+ this.callHistory = [];
139
+ this.eventListeners.clear();
140
+ this.callHandlers.clear();
141
+ this.initializeDefaultHandlers();
142
+ }
143
+ }
144
+ exports.CoherentMock = CoherentMock;
145
+ /**
146
+ * Global Coherent mock instance
147
+ */
148
+ exports.coherentMock = CoherentMock.getInstance();
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Garmin SDK Adapter for Jest testing
3
+ *
4
+ * Minimal mocks for @microsoft/msfs-garminsdk required by StormScope components.
5
+ * Goal: exercise the unit-test framework, not re-implement Garmin SDK.
6
+ */
7
+ import { DisplayComponent, Subject } from './SDKAdapter';
8
+ export declare enum WeatherRadarOperatingMode {
9
+ Weather = "Weather"
10
+ }
11
+ export declare enum WeatherRadarScanMode {
12
+ Horizontal = "Horizontal"
13
+ }
14
+ export declare enum MapTerrainMode {
15
+ None = "None",
16
+ Absolute = "Absolute",
17
+ Relative = "Relative",
18
+ Ground = "Ground"
19
+ }
20
+ export declare enum MapOrientation {
21
+ NorthUp = "NorthUp"
22
+ }
23
+ export declare enum UnitsDistanceSettingMode {
24
+ Nautical = "Nautical"
25
+ }
26
+ export declare const GarminMapKeys: {
27
+ readonly Units: "Units";
28
+ readonly Range: "Range";
29
+ readonly Terrain: "Terrain";
30
+ readonly Declutter: "Declutter";
31
+ readonly Orientation: "Orientation";
32
+ readonly Nexrad: "Nexrad";
33
+ };
34
+ export declare class MapUnitsModule {
35
+ constructor(_settingManager?: any);
36
+ }
37
+ export declare class MapDeclutterModule {
38
+ }
39
+ export declare class MapNexradModule {
40
+ readonly showNexrad: Subject<boolean>;
41
+ }
42
+ export declare class MapOrientationModule {
43
+ readonly orientation: Subject<MapOrientation>;
44
+ }
45
+ export type MapTerrainColorsDefinition = any;
46
+ export declare class MapTerrainModule {
47
+ readonly terrainMode: Subject<MapTerrainMode>;
48
+ }
49
+ export type MapWxrControllerModules = any;
50
+ export declare class MapWxrController {
51
+ constructor(_context: any);
52
+ }
53
+ export declare class MapRangeController {
54
+ private readonly rangeValues;
55
+ private readonly nominalRange;
56
+ private rangeIndex;
57
+ constructor(rangeValues: any[] | undefined, nominalRange: Subject<any>);
58
+ changeRangeIndex(delta: number): void;
59
+ setRangeIndex(index: number): void;
60
+ }
61
+ export declare const GarminMapBuilder: {
62
+ readonly range: symbol;
63
+ readonly orientation: symbol;
64
+ readonly declutter: symbol;
65
+ readonly terrainColors: symbol;
66
+ };
67
+ export interface WeatherRadarProps {
68
+ bingId: string;
69
+ bus: any;
70
+ ref?: any;
71
+ [key: string]: any;
72
+ }
73
+ export declare class WeatherRadar extends DisplayComponent<WeatherRadarProps> {
74
+ rootElement: any;
75
+ update: jest.Mock<any, any, any>;
76
+ wake: jest.Mock<any, any, any>;
77
+ sleep: jest.Mock<any, any, any>;
78
+ render(): any;
79
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ /**
3
+ * Garmin SDK Adapter for Jest testing
4
+ *
5
+ * Minimal mocks for @microsoft/msfs-garminsdk required by StormScope components.
6
+ * Goal: exercise the unit-test framework, not re-implement Garmin SDK.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.WeatherRadar = exports.GarminMapBuilder = exports.MapRangeController = exports.MapWxrController = exports.MapTerrainModule = exports.MapOrientationModule = exports.MapNexradModule = exports.MapDeclutterModule = exports.MapUnitsModule = exports.GarminMapKeys = exports.UnitsDistanceSettingMode = exports.MapOrientation = exports.MapTerrainMode = exports.WeatherRadarScanMode = exports.WeatherRadarOperatingMode = void 0;
10
+ // IMPORTANT: This adapter is part of the test framework and should not depend on
11
+ // the real SDK runtime/types. Import from our own SDKAdapter instead.
12
+ const SDKAdapter_1 = require("./SDKAdapter");
13
+ // -----------------------------
14
+ // Enums / constants
15
+ // -----------------------------
16
+ var WeatherRadarOperatingMode;
17
+ (function (WeatherRadarOperatingMode) {
18
+ WeatherRadarOperatingMode["Weather"] = "Weather";
19
+ })(WeatherRadarOperatingMode || (exports.WeatherRadarOperatingMode = WeatherRadarOperatingMode = {}));
20
+ var WeatherRadarScanMode;
21
+ (function (WeatherRadarScanMode) {
22
+ WeatherRadarScanMode["Horizontal"] = "Horizontal";
23
+ })(WeatherRadarScanMode || (exports.WeatherRadarScanMode = WeatherRadarScanMode = {}));
24
+ var MapTerrainMode;
25
+ (function (MapTerrainMode) {
26
+ MapTerrainMode["None"] = "None";
27
+ MapTerrainMode["Absolute"] = "Absolute";
28
+ MapTerrainMode["Relative"] = "Relative";
29
+ MapTerrainMode["Ground"] = "Ground";
30
+ })(MapTerrainMode || (exports.MapTerrainMode = MapTerrainMode = {}));
31
+ var MapOrientation;
32
+ (function (MapOrientation) {
33
+ MapOrientation["NorthUp"] = "NorthUp";
34
+ })(MapOrientation || (exports.MapOrientation = MapOrientation = {}));
35
+ var UnitsDistanceSettingMode;
36
+ (function (UnitsDistanceSettingMode) {
37
+ UnitsDistanceSettingMode["Nautical"] = "Nautical";
38
+ })(UnitsDistanceSettingMode || (exports.UnitsDistanceSettingMode = UnitsDistanceSettingMode = {}));
39
+ exports.GarminMapKeys = {
40
+ Units: 'Units',
41
+ Range: 'Range',
42
+ Terrain: 'Terrain',
43
+ Declutter: 'Declutter',
44
+ Orientation: 'Orientation',
45
+ Nexrad: 'Nexrad',
46
+ };
47
+ // -----------------------------
48
+ // Module stubs used by map manager
49
+ // -----------------------------
50
+ class MapUnitsModule {
51
+ constructor(_settingManager) { }
52
+ }
53
+ exports.MapUnitsModule = MapUnitsModule;
54
+ class MapDeclutterModule {
55
+ }
56
+ exports.MapDeclutterModule = MapDeclutterModule;
57
+ class MapNexradModule {
58
+ constructor() {
59
+ this.showNexrad = SDKAdapter_1.Subject.create(false);
60
+ }
61
+ }
62
+ exports.MapNexradModule = MapNexradModule;
63
+ class MapOrientationModule {
64
+ constructor() {
65
+ this.orientation = SDKAdapter_1.Subject.create(MapOrientation.NorthUp);
66
+ }
67
+ }
68
+ exports.MapOrientationModule = MapOrientationModule;
69
+ class MapTerrainModule {
70
+ constructor() {
71
+ this.terrainMode = SDKAdapter_1.Subject.create(MapTerrainMode.Absolute);
72
+ }
73
+ }
74
+ exports.MapTerrainModule = MapTerrainModule;
75
+ class MapWxrController {
76
+ constructor(_context) { }
77
+ }
78
+ exports.MapWxrController = MapWxrController;
79
+ // -----------------------------
80
+ // Range controller used by StormScopeMapManager
81
+ // -----------------------------
82
+ class MapRangeController {
83
+ constructor(rangeValues = [], nominalRange) {
84
+ this.rangeValues = rangeValues;
85
+ this.nominalRange = nominalRange;
86
+ this.rangeIndex = 0;
87
+ }
88
+ changeRangeIndex(delta) {
89
+ this.setRangeIndex(this.rangeIndex + delta);
90
+ }
91
+ setRangeIndex(index) {
92
+ const clamped = Math.max(0, Math.min(index, this.rangeValues.length - 1));
93
+ this.rangeIndex = clamped;
94
+ const val = this.rangeValues[clamped];
95
+ if (val !== undefined) {
96
+ this.nominalRange.set(val);
97
+ }
98
+ }
99
+ }
100
+ exports.MapRangeController = MapRangeController;
101
+ // -----------------------------
102
+ // GarminMapBuilder tokens (consumed by MapSystemBuilder.with())
103
+ // -----------------------------
104
+ exports.GarminMapBuilder = {
105
+ range: Symbol('GarminMapBuilder.range'),
106
+ orientation: Symbol('GarminMapBuilder.orientation'),
107
+ declutter: Symbol('GarminMapBuilder.declutter'),
108
+ terrainColors: Symbol('GarminMapBuilder.terrainColors'),
109
+ };
110
+ class WeatherRadar extends SDKAdapter_1.DisplayComponent {
111
+ constructor() {
112
+ super(...arguments);
113
+ this.rootElement = null;
114
+ this.update = jest.fn();
115
+ this.wake = jest.fn();
116
+ this.sleep = jest.fn();
117
+ }
118
+ render() {
119
+ const vnode = SDKAdapter_1.FSComponent.buildComponent('div', {
120
+ id: `weather-radar-${this.props.bingId}`,
121
+ class: 'weather-radar',
122
+ });
123
+ this.rootElement = vnode?.instance ?? null;
124
+ return vnode;
125
+ }
126
+ }
127
+ exports.WeatherRadar = WeatherRadar;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Mock MSFS global objects and types required by SDK
3
+ *
4
+ * These must be set on global object BEFORE any SDK code loads
5
+ */
6
+ export declare function setupMSFSGlobals(): void;