@react-text-game/core 0.1.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/README.md +744 -0
- package/dist/baseGameObject.d.ts +90 -0
- package/dist/baseGameObject.d.ts.map +1 -0
- package/dist/baseGameObject.js +109 -0
- package/dist/baseGameObject.js.map +1 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +12 -0
- package/dist/constants.js.map +1 -0
- package/dist/game.d.ts +294 -0
- package/dist/game.d.ts.map +1 -0
- package/dist/game.js +489 -0
- package/dist/game.js.map +1 -0
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +6 -0
- package/dist/helpers.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/useCurrentPassage.d.ts +10 -0
- package/dist/hooks/useCurrentPassage.d.ts.map +1 -0
- package/dist/hooks/useCurrentPassage.js +17 -0
- package/dist/hooks/useCurrentPassage.js.map +1 -0
- package/dist/hooks/useGameEntity.d.ts +21 -0
- package/dist/hooks/useGameEntity.d.ts.map +1 -0
- package/dist/hooks/useGameEntity.js +70 -0
- package/dist/hooks/useGameEntity.js.map +1 -0
- package/dist/hooks/useGameIsStarted.d.ts +12 -0
- package/dist/hooks/useGameIsStarted.d.ts.map +1 -0
- package/dist/hooks/useGameIsStarted.js +18 -0
- package/dist/hooks/useGameIsStarted.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +36 -0
- package/dist/logger.js.map +1 -0
- package/dist/options.d.ts +13 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +15 -0
- package/dist/options.js.map +1 -0
- package/dist/passages/interactiveMap/fabric.d.ts +4 -0
- package/dist/passages/interactiveMap/fabric.d.ts.map +1 -0
- package/dist/passages/interactiveMap/fabric.js +3 -0
- package/dist/passages/interactiveMap/fabric.js.map +1 -0
- package/dist/passages/interactiveMap/index.d.ts +4 -0
- package/dist/passages/interactiveMap/index.d.ts.map +1 -0
- package/dist/passages/interactiveMap/index.js +4 -0
- package/dist/passages/interactiveMap/index.js.map +1 -0
- package/dist/passages/interactiveMap/interactiveMap.d.ts +89 -0
- package/dist/passages/interactiveMap/interactiveMap.d.ts.map +1 -0
- package/dist/passages/interactiveMap/interactiveMap.js +103 -0
- package/dist/passages/interactiveMap/interactiveMap.js.map +1 -0
- package/dist/passages/interactiveMap/types.d.ts +822 -0
- package/dist/passages/interactiveMap/types.d.ts.map +1 -0
- package/dist/passages/interactiveMap/types.js +2 -0
- package/dist/passages/interactiveMap/types.js.map +1 -0
- package/dist/passages/passage.d.ts +57 -0
- package/dist/passages/passage.d.ts.map +1 -0
- package/dist/passages/passage.js +64 -0
- package/dist/passages/passage.js.map +1 -0
- package/dist/passages/story/fabric.d.ts +4 -0
- package/dist/passages/story/fabric.d.ts.map +1 -0
- package/dist/passages/story/fabric.js +3 -0
- package/dist/passages/story/fabric.js.map +1 -0
- package/dist/passages/story/index.d.ts +5 -0
- package/dist/passages/story/index.d.ts.map +1 -0
- package/dist/passages/story/index.js +5 -0
- package/dist/passages/story/index.js.map +1 -0
- package/dist/passages/story/start.d.ts +14 -0
- package/dist/passages/story/start.d.ts.map +1 -0
- package/dist/passages/story/start.js +22 -0
- package/dist/passages/story/start.js.map +1 -0
- package/dist/passages/story/story.d.ts +84 -0
- package/dist/passages/story/story.d.ts.map +1 -0
- package/dist/passages/story/story.js +88 -0
- package/dist/passages/story/story.js.map +1 -0
- package/dist/passages/story/types.d.ts +911 -0
- package/dist/passages/story/types.d.ts.map +1 -0
- package/dist/passages/story/types.js +2 -0
- package/dist/passages/story/types.js.map +1 -0
- package/dist/passages/types/index.d.ts +3 -0
- package/dist/passages/types/index.d.ts.map +1 -0
- package/dist/passages/types/index.js +2 -0
- package/dist/passages/types/index.js.map +1 -0
- package/dist/passages/widget.d.ts +62 -0
- package/dist/passages/widget.d.ts.map +1 -0
- package/dist/passages/widget.js +66 -0
- package/dist/passages/widget.js.map +1 -0
- package/dist/saves/constants.d.ts +17 -0
- package/dist/saves/constants.d.ts.map +1 -0
- package/dist/saves/constants.js +17 -0
- package/dist/saves/constants.js.map +1 -0
- package/dist/saves/db.d.ts +119 -0
- package/dist/saves/db.d.ts.map +1 -0
- package/dist/saves/db.js +231 -0
- package/dist/saves/db.js.map +1 -0
- package/dist/saves/helpers.d.ts +28 -0
- package/dist/saves/helpers.d.ts.map +1 -0
- package/dist/saves/helpers.js +84 -0
- package/dist/saves/helpers.js.map +1 -0
- package/dist/saves/hooks/index.d.ts +10 -0
- package/dist/saves/hooks/index.d.ts.map +1 -0
- package/dist/saves/hooks/index.js +10 -0
- package/dist/saves/hooks/index.js.map +1 -0
- package/dist/saves/hooks/useDeleteAllSlots.d.ts +18 -0
- package/dist/saves/hooks/useDeleteAllSlots.d.ts.map +1 -0
- package/dist/saves/hooks/useDeleteAllSlots.js +18 -0
- package/dist/saves/hooks/useDeleteAllSlots.js.map +1 -0
- package/dist/saves/hooks/useDeleteGame.d.ts +22 -0
- package/dist/saves/hooks/useDeleteGame.d.ts.map +1 -0
- package/dist/saves/hooks/useDeleteGame.js +33 -0
- package/dist/saves/hooks/useDeleteGame.js.map +1 -0
- package/dist/saves/hooks/useExportSaves.d.ts +27 -0
- package/dist/saves/hooks/useExportSaves.d.ts.map +1 -0
- package/dist/saves/hooks/useExportSaves.js +54 -0
- package/dist/saves/hooks/useExportSaves.js.map +1 -0
- package/dist/saves/hooks/useImportSaves.d.ts +29 -0
- package/dist/saves/hooks/useImportSaves.d.ts.map +1 -0
- package/dist/saves/hooks/useImportSaves.js +108 -0
- package/dist/saves/hooks/useImportSaves.js.map +1 -0
- package/dist/saves/hooks/useLastLoadGame.d.ts +39 -0
- package/dist/saves/hooks/useLastLoadGame.d.ts.map +1 -0
- package/dist/saves/hooks/useLastLoadGame.js +72 -0
- package/dist/saves/hooks/useLastLoadGame.js.map +1 -0
- package/dist/saves/hooks/useLoadGame.d.ts +22 -0
- package/dist/saves/hooks/useLoadGame.d.ts.map +1 -0
- package/dist/saves/hooks/useLoadGame.js +40 -0
- package/dist/saves/hooks/useLoadGame.js.map +1 -0
- package/dist/saves/hooks/useRestartGame.d.ts +20 -0
- package/dist/saves/hooks/useRestartGame.d.ts.map +1 -0
- package/dist/saves/hooks/useRestartGame.js +29 -0
- package/dist/saves/hooks/useRestartGame.js.map +1 -0
- package/dist/saves/hooks/useSaveGame.d.ts +22 -0
- package/dist/saves/hooks/useSaveGame.d.ts.map +1 -0
- package/dist/saves/hooks/useSaveGame.js +34 -0
- package/dist/saves/hooks/useSaveGame.js.map +1 -0
- package/dist/saves/hooks/useSaveSlots.d.ts +45 -0
- package/dist/saves/hooks/useSaveSlots.d.ts.map +1 -0
- package/dist/saves/hooks/useSaveSlots.js +42 -0
- package/dist/saves/hooks/useSaveSlots.js.map +1 -0
- package/dist/saves/index.d.ts +4 -0
- package/dist/saves/index.d.ts.map +1 -0
- package/dist/saves/index.js +3 -0
- package/dist/saves/index.js.map +1 -0
- package/dist/saves/types.d.ts +52 -0
- package/dist/saves/types.d.ts.map +1 -0
- package/dist/saves/types.js +2 -0
- package/dist/saves/types.js.map +1 -0
- package/dist/storage.d.ts +124 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +229 -0
- package/dist/storage.js.map +1 -0
- package/dist/tests/game.test.d.ts +2 -0
- package/dist/tests/game.test.d.ts.map +1 -0
- package/dist/tests/game.test.js +602 -0
- package/dist/tests/game.test.js.map +1 -0
- package/dist/tests/interactiveMap.test.d.ts +2 -0
- package/dist/tests/interactiveMap.test.d.ts.map +1 -0
- package/dist/tests/interactiveMap.test.js +1003 -0
- package/dist/tests/interactiveMap.test.js.map +1 -0
- package/dist/tests/storage.test.d.ts +2 -0
- package/dist/tests/storage.test.d.ts.map +1 -0
- package/dist/tests/storage.test.js +328 -0
- package/dist/tests/storage.test.js.map +1 -0
- package/dist/tests/story.test.d.ts +2 -0
- package/dist/tests/story.test.d.ts.map +1 -0
- package/dist/tests/story.test.js +698 -0
- package/dist/tests/story.test.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { Game } from "../game";
|
|
3
|
+
import { newInteractiveMap } from "../passages/interactiveMap/fabric";
|
|
4
|
+
import { InteractiveMap } from "../passages/interactiveMap/interactiveMap";
|
|
5
|
+
import { Storage } from "../storage";
|
|
6
|
+
// Counter for unique IDs
|
|
7
|
+
let testCounter = 0;
|
|
8
|
+
function uniqueId(prefix) {
|
|
9
|
+
return `${prefix}-${testCounter++}`;
|
|
10
|
+
}
|
|
11
|
+
describe("InteractiveMap", () => {
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
// Clear storage
|
|
14
|
+
Storage.setState({});
|
|
15
|
+
// Re-initialize the game for each test
|
|
16
|
+
await Game.init({ gameName: "Test Game", isDevMode: true });
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
// Reset game state after each test
|
|
20
|
+
Game._resetForTesting();
|
|
21
|
+
});
|
|
22
|
+
describe("Constructor", () => {
|
|
23
|
+
test("creates an interactive map with id and options", () => {
|
|
24
|
+
const id = uniqueId("map");
|
|
25
|
+
const options = {
|
|
26
|
+
image: "/map.jpg",
|
|
27
|
+
hotspots: [],
|
|
28
|
+
};
|
|
29
|
+
const map = new InteractiveMap(id, options);
|
|
30
|
+
expect(map.id).toBe(id);
|
|
31
|
+
expect(map.type).toBe("interactiveMap");
|
|
32
|
+
});
|
|
33
|
+
test("creates a map with complete options", () => {
|
|
34
|
+
const id = uniqueId("map");
|
|
35
|
+
const options = {
|
|
36
|
+
caption: "World Map",
|
|
37
|
+
image: "/map.jpg",
|
|
38
|
+
bgImage: "/bg.jpg",
|
|
39
|
+
hotspots: [],
|
|
40
|
+
props: { bgOpacity: 0.5 },
|
|
41
|
+
classNames: { container: "custom-container" },
|
|
42
|
+
};
|
|
43
|
+
const map = new InteractiveMap(id, options);
|
|
44
|
+
expect(map.id).toBe(id);
|
|
45
|
+
expect(map.type).toBe("interactiveMap");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("Factory Function", () => {
|
|
49
|
+
test("newInteractiveMap creates an InteractiveMap instance", () => {
|
|
50
|
+
const id = uniqueId("map");
|
|
51
|
+
const options = {
|
|
52
|
+
image: "/map.jpg",
|
|
53
|
+
hotspots: [],
|
|
54
|
+
};
|
|
55
|
+
const map = newInteractiveMap(id, options);
|
|
56
|
+
expect(map).toBeInstanceOf(InteractiveMap);
|
|
57
|
+
expect(map.id).toBe(id);
|
|
58
|
+
});
|
|
59
|
+
test("newInteractiveMap with full options", () => {
|
|
60
|
+
const id = uniqueId("map");
|
|
61
|
+
const options = {
|
|
62
|
+
caption: "Test Map",
|
|
63
|
+
image: "/test.jpg",
|
|
64
|
+
bgImage: "/bg.jpg",
|
|
65
|
+
hotspots: [],
|
|
66
|
+
};
|
|
67
|
+
const map = newInteractiveMap(id, options);
|
|
68
|
+
expect(map).toBeInstanceOf(InteractiveMap);
|
|
69
|
+
expect(map.id).toBe(id);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("Display Method - Basic", () => {
|
|
73
|
+
test("displays map with static image", () => {
|
|
74
|
+
const options = {
|
|
75
|
+
image: "/world-map.jpg",
|
|
76
|
+
hotspots: [],
|
|
77
|
+
};
|
|
78
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
79
|
+
const result = map.display();
|
|
80
|
+
expect(result.image).toBe("/world-map.jpg");
|
|
81
|
+
expect(result.hotspots).toHaveLength(0);
|
|
82
|
+
});
|
|
83
|
+
test("displays map with dynamic image function", () => {
|
|
84
|
+
const options = {
|
|
85
|
+
image: () => "/dynamic-map.jpg",
|
|
86
|
+
hotspots: [],
|
|
87
|
+
};
|
|
88
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
89
|
+
const result = map.display();
|
|
90
|
+
expect(result.image).toBe("/dynamic-map.jpg");
|
|
91
|
+
});
|
|
92
|
+
test("displays map with background image", () => {
|
|
93
|
+
const options = {
|
|
94
|
+
image: "/map.jpg",
|
|
95
|
+
bgImage: "/background.jpg",
|
|
96
|
+
hotspots: [],
|
|
97
|
+
};
|
|
98
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
99
|
+
const result = map.display();
|
|
100
|
+
expect(result.image).toBe("/map.jpg");
|
|
101
|
+
expect(result.bgImage).toBe("/background.jpg");
|
|
102
|
+
});
|
|
103
|
+
test("displays map with dynamic background image", () => {
|
|
104
|
+
const options = {
|
|
105
|
+
image: "/map.jpg",
|
|
106
|
+
bgImage: () => "/dynamic-bg.jpg",
|
|
107
|
+
hotspots: [],
|
|
108
|
+
};
|
|
109
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
110
|
+
const result = map.display();
|
|
111
|
+
expect(result.bgImage).toBe("/dynamic-bg.jpg");
|
|
112
|
+
});
|
|
113
|
+
test("displays map with caption", () => {
|
|
114
|
+
const options = {
|
|
115
|
+
caption: "Kingdom Map",
|
|
116
|
+
image: "/map.jpg",
|
|
117
|
+
hotspots: [],
|
|
118
|
+
};
|
|
119
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
120
|
+
const result = map.display();
|
|
121
|
+
expect(result.caption).toBe("Kingdom Map");
|
|
122
|
+
});
|
|
123
|
+
test("displays map with props", () => {
|
|
124
|
+
const options = {
|
|
125
|
+
image: "/map.jpg",
|
|
126
|
+
hotspots: [],
|
|
127
|
+
props: { bgOpacity: 0.7 },
|
|
128
|
+
};
|
|
129
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
130
|
+
const result = map.display();
|
|
131
|
+
expect(result.props?.bgOpacity).toBe(0.7);
|
|
132
|
+
});
|
|
133
|
+
test("displays map with classNames", () => {
|
|
134
|
+
const options = {
|
|
135
|
+
image: "/map.jpg",
|
|
136
|
+
hotspots: [],
|
|
137
|
+
classNames: {
|
|
138
|
+
container: "container-class",
|
|
139
|
+
topHotspots: "top-class",
|
|
140
|
+
bottomHotspots: "bottom-class",
|
|
141
|
+
leftHotspots: "left-class",
|
|
142
|
+
rightHotspots: "right-class",
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
146
|
+
const result = map.display();
|
|
147
|
+
expect(result.classNames?.container).toBe("container-class");
|
|
148
|
+
expect(result.classNames?.topHotspots).toBe("top-class");
|
|
149
|
+
expect(result.classNames?.bottomHotspots).toBe("bottom-class");
|
|
150
|
+
expect(result.classNames?.leftHotspots).toBe("left-class");
|
|
151
|
+
expect(result.classNames?.rightHotspots).toBe("right-class");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe("Hotspot Types - Map Label", () => {
|
|
155
|
+
test("displays map with label hotspot", () => {
|
|
156
|
+
const hotspot = {
|
|
157
|
+
type: "label",
|
|
158
|
+
content: "Village",
|
|
159
|
+
position: { x: 30, y: 40 },
|
|
160
|
+
action: () => { },
|
|
161
|
+
};
|
|
162
|
+
const options = {
|
|
163
|
+
image: "/map.jpg",
|
|
164
|
+
hotspots: [hotspot],
|
|
165
|
+
};
|
|
166
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
167
|
+
const result = map.display();
|
|
168
|
+
expect(result.hotspots).toHaveLength(1);
|
|
169
|
+
expect(result.hotspots[0]?.type).toBe("label");
|
|
170
|
+
expect(result.hotspots[0].content).toBe("Village");
|
|
171
|
+
expect(result.hotspots[0].position).toEqual({
|
|
172
|
+
x: 30,
|
|
173
|
+
y: 40,
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
test("label hotspot with dynamic content", () => {
|
|
177
|
+
const hotspot = {
|
|
178
|
+
type: "label",
|
|
179
|
+
content: () => "Dynamic Label",
|
|
180
|
+
position: { x: 50, y: 50 },
|
|
181
|
+
action: () => { },
|
|
182
|
+
};
|
|
183
|
+
const options = {
|
|
184
|
+
image: "/map.jpg",
|
|
185
|
+
hotspots: [hotspot],
|
|
186
|
+
};
|
|
187
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
188
|
+
const result = map.display();
|
|
189
|
+
// Dynamic content remains as function (resolved by UI layer)
|
|
190
|
+
const resultHotspot = result.hotspots[0];
|
|
191
|
+
expect(typeof resultHotspot.content).toBe("function");
|
|
192
|
+
if (typeof resultHotspot.content === "function") {
|
|
193
|
+
expect(resultHotspot.content()).toBe("Dynamic Label");
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
test("label hotspot with dynamic position", () => {
|
|
197
|
+
const hotspot = {
|
|
198
|
+
type: "label",
|
|
199
|
+
content: "Test",
|
|
200
|
+
position: { x: () => 25, y: () => 75 },
|
|
201
|
+
action: () => { },
|
|
202
|
+
};
|
|
203
|
+
const options = {
|
|
204
|
+
image: "/map.jpg",
|
|
205
|
+
hotspots: [hotspot],
|
|
206
|
+
};
|
|
207
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
208
|
+
const result = map.display();
|
|
209
|
+
const pos = result.hotspots[0].position;
|
|
210
|
+
expect(typeof pos.x).toBe("function");
|
|
211
|
+
expect(typeof pos.y).toBe("function");
|
|
212
|
+
if (typeof pos.x === "function" && typeof pos.y === "function") {
|
|
213
|
+
expect(pos.x()).toBe(25);
|
|
214
|
+
expect(pos.y()).toBe(75);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
test("label hotspot with all properties", () => {
|
|
218
|
+
const hotspot = {
|
|
219
|
+
type: "label",
|
|
220
|
+
id: "village-hotspot",
|
|
221
|
+
content: "Village",
|
|
222
|
+
position: { x: 30, y: 40 },
|
|
223
|
+
action: () => { },
|
|
224
|
+
isDisabled: false,
|
|
225
|
+
tooltip: {
|
|
226
|
+
content: "Click to visit village",
|
|
227
|
+
position: "top",
|
|
228
|
+
},
|
|
229
|
+
props: {
|
|
230
|
+
variant: "bordered",
|
|
231
|
+
color: "primary",
|
|
232
|
+
classNames: { button: "custom-button" },
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
const options = {
|
|
236
|
+
image: "/map.jpg",
|
|
237
|
+
hotspots: [hotspot],
|
|
238
|
+
};
|
|
239
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
240
|
+
const result = map.display();
|
|
241
|
+
const resultHotspot = result.hotspots[0];
|
|
242
|
+
expect(resultHotspot.id).toBe("village-hotspot");
|
|
243
|
+
expect(resultHotspot.isDisabled).toBe(false);
|
|
244
|
+
expect(resultHotspot.tooltip?.content).toBe("Click to visit village");
|
|
245
|
+
expect(resultHotspot.tooltip?.position).toBe("top");
|
|
246
|
+
expect(resultHotspot.props?.variant).toBe("bordered");
|
|
247
|
+
expect(resultHotspot.props?.color).toBe("primary");
|
|
248
|
+
expect(resultHotspot.props?.classNames?.button).toBe("custom-button");
|
|
249
|
+
});
|
|
250
|
+
test("label hotspot with dynamic disabled state", () => {
|
|
251
|
+
const hotspot = {
|
|
252
|
+
type: "label",
|
|
253
|
+
content: "Shop",
|
|
254
|
+
position: { x: 50, y: 50 },
|
|
255
|
+
action: () => { },
|
|
256
|
+
isDisabled: () => true,
|
|
257
|
+
};
|
|
258
|
+
const options = {
|
|
259
|
+
image: "/map.jpg",
|
|
260
|
+
hotspots: [hotspot],
|
|
261
|
+
};
|
|
262
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
263
|
+
const result = map.display();
|
|
264
|
+
const resultHotspot = result.hotspots[0];
|
|
265
|
+
expect(typeof resultHotspot.isDisabled).toBe("function");
|
|
266
|
+
if (typeof resultHotspot.isDisabled === "function") {
|
|
267
|
+
expect(resultHotspot.isDisabled()).toBe(true);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
test("label hotspot with dynamic tooltip", () => {
|
|
271
|
+
const hotspot = {
|
|
272
|
+
type: "label",
|
|
273
|
+
content: "Door",
|
|
274
|
+
position: { x: 50, y: 50 },
|
|
275
|
+
action: () => { },
|
|
276
|
+
tooltip: {
|
|
277
|
+
content: () => "Dynamic tooltip",
|
|
278
|
+
position: "bottom",
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
const options = {
|
|
282
|
+
image: "/map.jpg",
|
|
283
|
+
hotspots: [hotspot],
|
|
284
|
+
};
|
|
285
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
286
|
+
const result = map.display();
|
|
287
|
+
const resultHotspot = result.hotspots[0];
|
|
288
|
+
expect(typeof resultHotspot.tooltip?.content).toBe("function");
|
|
289
|
+
if (typeof resultHotspot.tooltip?.content === "function") {
|
|
290
|
+
expect(resultHotspot.tooltip.content()).toBe("Dynamic tooltip");
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
describe("Hotspot Types - Map Image", () => {
|
|
295
|
+
test("displays map with image hotspot", () => {
|
|
296
|
+
const hotspot = {
|
|
297
|
+
type: "image",
|
|
298
|
+
content: { idle: "/icon.png" },
|
|
299
|
+
position: { x: 60, y: 70 },
|
|
300
|
+
action: () => { },
|
|
301
|
+
};
|
|
302
|
+
const options = {
|
|
303
|
+
image: "/map.jpg",
|
|
304
|
+
hotspots: [hotspot],
|
|
305
|
+
};
|
|
306
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
307
|
+
const result = map.display();
|
|
308
|
+
expect(result.hotspots).toHaveLength(1);
|
|
309
|
+
expect(result.hotspots[0]?.type).toBe("image");
|
|
310
|
+
expect(result.hotspots[0].content.idle).toBe("/icon.png");
|
|
311
|
+
});
|
|
312
|
+
test("image hotspot with all image states", () => {
|
|
313
|
+
const hotspot = {
|
|
314
|
+
type: "image",
|
|
315
|
+
content: {
|
|
316
|
+
idle: "/idle.png",
|
|
317
|
+
hover: "/hover.png",
|
|
318
|
+
active: "/active.png",
|
|
319
|
+
disabled: "/disabled.png",
|
|
320
|
+
},
|
|
321
|
+
position: { x: 50, y: 50 },
|
|
322
|
+
action: () => { },
|
|
323
|
+
};
|
|
324
|
+
const options = {
|
|
325
|
+
image: "/map.jpg",
|
|
326
|
+
hotspots: [hotspot],
|
|
327
|
+
};
|
|
328
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
329
|
+
const result = map.display();
|
|
330
|
+
const resultHotspot = result.hotspots[0];
|
|
331
|
+
expect(resultHotspot.content.idle).toBe("/idle.png");
|
|
332
|
+
expect(resultHotspot.content.hover).toBe("/hover.png");
|
|
333
|
+
expect(resultHotspot.content.active).toBe("/active.png");
|
|
334
|
+
expect(resultHotspot.content.disabled).toBe("/disabled.png");
|
|
335
|
+
});
|
|
336
|
+
test("image hotspot with dynamic images", () => {
|
|
337
|
+
const hotspot = {
|
|
338
|
+
type: "image",
|
|
339
|
+
content: {
|
|
340
|
+
idle: () => "/dynamic-idle.png",
|
|
341
|
+
hover: () => "/dynamic-hover.png",
|
|
342
|
+
},
|
|
343
|
+
position: { x: 50, y: 50 },
|
|
344
|
+
action: () => { },
|
|
345
|
+
};
|
|
346
|
+
const options = {
|
|
347
|
+
image: "/map.jpg",
|
|
348
|
+
hotspots: [hotspot],
|
|
349
|
+
};
|
|
350
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
351
|
+
const result = map.display();
|
|
352
|
+
const resultHotspot = result.hotspots[0];
|
|
353
|
+
expect(typeof resultHotspot.content.idle).toBe("function");
|
|
354
|
+
expect(typeof resultHotspot.content.hover).toBe("function");
|
|
355
|
+
});
|
|
356
|
+
test("image hotspot with zoom and classNames", () => {
|
|
357
|
+
const hotspot = {
|
|
358
|
+
type: "image",
|
|
359
|
+
content: { idle: "/icon.png" },
|
|
360
|
+
position: { x: 50, y: 50 },
|
|
361
|
+
action: () => { },
|
|
362
|
+
props: {
|
|
363
|
+
zoom: "150%",
|
|
364
|
+
classNames: {
|
|
365
|
+
container: "custom-container",
|
|
366
|
+
idle: "idle-class",
|
|
367
|
+
hover: "hover-class",
|
|
368
|
+
active: "active-class",
|
|
369
|
+
disabled: "disabled-class",
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
const options = {
|
|
374
|
+
image: "/map.jpg",
|
|
375
|
+
hotspots: [hotspot],
|
|
376
|
+
};
|
|
377
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
378
|
+
const result = map.display();
|
|
379
|
+
const resultHotspot = result.hotspots[0];
|
|
380
|
+
expect(resultHotspot.props?.zoom).toBe("150%");
|
|
381
|
+
expect(resultHotspot.props?.classNames?.container).toBe("custom-container");
|
|
382
|
+
expect(resultHotspot.props?.classNames?.idle).toBe("idle-class");
|
|
383
|
+
expect(resultHotspot.props?.classNames?.hover).toBe("hover-class");
|
|
384
|
+
expect(resultHotspot.props?.classNames?.active).toBe("active-class");
|
|
385
|
+
expect(resultHotspot.props?.classNames?.disabled).toBe("disabled-class");
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
describe("Hotspot Types - Side Label", () => {
|
|
389
|
+
test("displays map with side label hotspot - top", () => {
|
|
390
|
+
const hotspot = {
|
|
391
|
+
type: "label",
|
|
392
|
+
content: "Menu",
|
|
393
|
+
position: "top",
|
|
394
|
+
action: () => { },
|
|
395
|
+
};
|
|
396
|
+
const options = {
|
|
397
|
+
image: "/map.jpg",
|
|
398
|
+
hotspots: [hotspot],
|
|
399
|
+
};
|
|
400
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
401
|
+
const result = map.display();
|
|
402
|
+
expect(result.hotspots).toHaveLength(1);
|
|
403
|
+
expect(result.hotspots[0].position).toBe("top");
|
|
404
|
+
});
|
|
405
|
+
test("side label hotspots on all sides", () => {
|
|
406
|
+
const options = {
|
|
407
|
+
image: "/map.jpg",
|
|
408
|
+
hotspots: [
|
|
409
|
+
{
|
|
410
|
+
type: "label",
|
|
411
|
+
content: "Top",
|
|
412
|
+
position: "top",
|
|
413
|
+
action: () => { },
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
type: "label",
|
|
417
|
+
content: "Bottom",
|
|
418
|
+
position: "bottom",
|
|
419
|
+
action: () => { },
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
type: "label",
|
|
423
|
+
content: "Left",
|
|
424
|
+
position: "left",
|
|
425
|
+
action: () => { },
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
type: "label",
|
|
429
|
+
content: "Right",
|
|
430
|
+
position: "right",
|
|
431
|
+
action: () => { },
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
};
|
|
435
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
436
|
+
const result = map.display();
|
|
437
|
+
expect(result.hotspots).toHaveLength(4);
|
|
438
|
+
expect(result.hotspots[0].position).toBe("top");
|
|
439
|
+
expect(result.hotspots[1].position).toBe("bottom");
|
|
440
|
+
expect(result.hotspots[2].position).toBe("left");
|
|
441
|
+
expect(result.hotspots[3].position).toBe("right");
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
describe("Hotspot Types - Side Image", () => {
|
|
445
|
+
test("displays map with side image hotspot", () => {
|
|
446
|
+
const hotspot = {
|
|
447
|
+
type: "image",
|
|
448
|
+
content: { idle: "/compass.png" },
|
|
449
|
+
position: "bottom",
|
|
450
|
+
action: () => { },
|
|
451
|
+
};
|
|
452
|
+
const options = {
|
|
453
|
+
image: "/map.jpg",
|
|
454
|
+
hotspots: [hotspot],
|
|
455
|
+
};
|
|
456
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
457
|
+
const result = map.display();
|
|
458
|
+
expect(result.hotspots).toHaveLength(1);
|
|
459
|
+
expect(result.hotspots[0].position).toBe("bottom");
|
|
460
|
+
expect(result.hotspots[0].content.idle).toBe("/compass.png");
|
|
461
|
+
});
|
|
462
|
+
test("side image hotspots with all image states", () => {
|
|
463
|
+
const hotspot = {
|
|
464
|
+
type: "image",
|
|
465
|
+
content: {
|
|
466
|
+
idle: "/idle.png",
|
|
467
|
+
hover: "/hover.png",
|
|
468
|
+
active: "/active.png",
|
|
469
|
+
disabled: "/disabled.png",
|
|
470
|
+
},
|
|
471
|
+
position: "left",
|
|
472
|
+
action: () => { },
|
|
473
|
+
};
|
|
474
|
+
const options = {
|
|
475
|
+
image: "/map.jpg",
|
|
476
|
+
hotspots: [hotspot],
|
|
477
|
+
};
|
|
478
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
479
|
+
const result = map.display();
|
|
480
|
+
const resultHotspot = result.hotspots[0];
|
|
481
|
+
expect(resultHotspot.content.idle).toBe("/idle.png");
|
|
482
|
+
expect(resultHotspot.content.hover).toBe("/hover.png");
|
|
483
|
+
expect(resultHotspot.content.active).toBe("/active.png");
|
|
484
|
+
expect(resultHotspot.content.disabled).toBe("/disabled.png");
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
describe("Hotspot Types - Menu", () => {
|
|
488
|
+
test("displays map with menu hotspot", () => {
|
|
489
|
+
const menu = {
|
|
490
|
+
type: "menu",
|
|
491
|
+
position: { x: 50, y: 50 },
|
|
492
|
+
items: [
|
|
493
|
+
{
|
|
494
|
+
type: "label",
|
|
495
|
+
content: "Option 1",
|
|
496
|
+
action: () => { },
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
type: "label",
|
|
500
|
+
content: "Option 2",
|
|
501
|
+
action: () => { },
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
};
|
|
505
|
+
const options = {
|
|
506
|
+
image: "/map.jpg",
|
|
507
|
+
hotspots: [menu],
|
|
508
|
+
};
|
|
509
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
510
|
+
const result = map.display();
|
|
511
|
+
expect(result.hotspots).toHaveLength(1);
|
|
512
|
+
expect(result.hotspots[0]?.type).toBe("menu");
|
|
513
|
+
expect(result.hotspots[0].items).toHaveLength(2);
|
|
514
|
+
});
|
|
515
|
+
test("menu with direction and className", () => {
|
|
516
|
+
const menu = {
|
|
517
|
+
type: "menu",
|
|
518
|
+
position: { x: 50, y: 50 },
|
|
519
|
+
direction: "horizontal",
|
|
520
|
+
items: [
|
|
521
|
+
{
|
|
522
|
+
type: "label",
|
|
523
|
+
content: "Item 1",
|
|
524
|
+
action: () => { },
|
|
525
|
+
},
|
|
526
|
+
],
|
|
527
|
+
props: { className: "menu-class" },
|
|
528
|
+
};
|
|
529
|
+
const options = {
|
|
530
|
+
image: "/map.jpg",
|
|
531
|
+
hotspots: [menu],
|
|
532
|
+
};
|
|
533
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
534
|
+
const result = map.display();
|
|
535
|
+
const resultMenu = result.hotspots[0];
|
|
536
|
+
expect(resultMenu.direction).toBe("horizontal");
|
|
537
|
+
expect(resultMenu.props?.className).toBe("menu-class");
|
|
538
|
+
});
|
|
539
|
+
test("menu with dynamic position", () => {
|
|
540
|
+
const menu = {
|
|
541
|
+
type: "menu",
|
|
542
|
+
position: { x: () => 25, y: () => 75 },
|
|
543
|
+
items: [
|
|
544
|
+
{
|
|
545
|
+
type: "label",
|
|
546
|
+
content: "Item",
|
|
547
|
+
action: () => { },
|
|
548
|
+
},
|
|
549
|
+
],
|
|
550
|
+
};
|
|
551
|
+
const options = {
|
|
552
|
+
image: "/map.jpg",
|
|
553
|
+
hotspots: [menu],
|
|
554
|
+
};
|
|
555
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
556
|
+
const result = map.display();
|
|
557
|
+
const resultMenu = result.hotspots[0];
|
|
558
|
+
expect(typeof resultMenu.position.x).toBe("function");
|
|
559
|
+
expect(typeof resultMenu.position.y).toBe("function");
|
|
560
|
+
if (typeof resultMenu.position.x === "function" &&
|
|
561
|
+
typeof resultMenu.position.y === "function") {
|
|
562
|
+
expect(resultMenu.position.x()).toBe(25);
|
|
563
|
+
expect(resultMenu.position.y()).toBe(75);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
test("menu with conditional items", () => {
|
|
567
|
+
const menu = {
|
|
568
|
+
type: "menu",
|
|
569
|
+
position: { x: 50, y: 50 },
|
|
570
|
+
items: [
|
|
571
|
+
{
|
|
572
|
+
type: "label",
|
|
573
|
+
content: "Always",
|
|
574
|
+
action: () => { },
|
|
575
|
+
},
|
|
576
|
+
() => undefined, // Conditional item that's filtered out
|
|
577
|
+
{
|
|
578
|
+
type: "label",
|
|
579
|
+
content: "Also always",
|
|
580
|
+
action: () => { },
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
};
|
|
584
|
+
const options = {
|
|
585
|
+
image: "/map.jpg",
|
|
586
|
+
hotspots: [menu],
|
|
587
|
+
};
|
|
588
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
589
|
+
const result = map.display();
|
|
590
|
+
const resultMenu = result.hotspots[0];
|
|
591
|
+
// The menu itself is not filtered, but the conditional logic
|
|
592
|
+
// would happen at render time in the UI
|
|
593
|
+
expect(resultMenu.items).toHaveLength(3);
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
describe("Dynamic Hotspots", () => {
|
|
597
|
+
test("filters out undefined hotspots", () => {
|
|
598
|
+
const options = {
|
|
599
|
+
image: "/map.jpg",
|
|
600
|
+
hotspots: [
|
|
601
|
+
{
|
|
602
|
+
type: "label",
|
|
603
|
+
content: "Visible",
|
|
604
|
+
position: { x: 50, y: 50 },
|
|
605
|
+
action: () => { },
|
|
606
|
+
},
|
|
607
|
+
() => undefined, // Conditional hotspot
|
|
608
|
+
{
|
|
609
|
+
type: "label",
|
|
610
|
+
content: "Also visible",
|
|
611
|
+
position: { x: 60, y: 60 },
|
|
612
|
+
action: () => { },
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
};
|
|
616
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
617
|
+
const result = map.display();
|
|
618
|
+
expect(result.hotspots).toHaveLength(2);
|
|
619
|
+
expect(result.hotspots[0].content).toBe("Visible");
|
|
620
|
+
expect(result.hotspots[1].content).toBe("Also visible");
|
|
621
|
+
});
|
|
622
|
+
test("processes hotspot functions with props", () => {
|
|
623
|
+
let receivedProps = null;
|
|
624
|
+
const options = {
|
|
625
|
+
image: "/map.jpg",
|
|
626
|
+
hotspots: [
|
|
627
|
+
// @ts-expect-error TS2322
|
|
628
|
+
(props) => {
|
|
629
|
+
receivedProps = props;
|
|
630
|
+
return {
|
|
631
|
+
type: "label",
|
|
632
|
+
content: "Test",
|
|
633
|
+
position: { x: 50, y: 50 },
|
|
634
|
+
action: () => { },
|
|
635
|
+
};
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
};
|
|
639
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
640
|
+
const testProps = { hasKey: true, level: 5 };
|
|
641
|
+
map.display(testProps);
|
|
642
|
+
expect(receivedProps).toEqual(testProps);
|
|
643
|
+
});
|
|
644
|
+
test("conditional hotspot based on props", () => {
|
|
645
|
+
const options = {
|
|
646
|
+
image: "/map.jpg",
|
|
647
|
+
hotspots: [
|
|
648
|
+
// @ts-expect-error TS2322
|
|
649
|
+
(props) => props.hasKey
|
|
650
|
+
? {
|
|
651
|
+
type: "label",
|
|
652
|
+
content: "Secret Door",
|
|
653
|
+
position: { x: 50, y: 50 },
|
|
654
|
+
action: () => { },
|
|
655
|
+
}
|
|
656
|
+
: undefined,
|
|
657
|
+
],
|
|
658
|
+
};
|
|
659
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
660
|
+
const resultWithKey = map.display({ hasKey: true });
|
|
661
|
+
expect(resultWithKey.hotspots).toHaveLength(1);
|
|
662
|
+
const resultWithoutKey = map.display({ hasKey: false });
|
|
663
|
+
expect(resultWithoutKey.hotspots).toHaveLength(0);
|
|
664
|
+
});
|
|
665
|
+
test("multiple conditional hotspots", () => {
|
|
666
|
+
const options = {
|
|
667
|
+
image: "/map.jpg",
|
|
668
|
+
hotspots: [
|
|
669
|
+
// @ts-expect-error TS2322
|
|
670
|
+
(props) => props.showA
|
|
671
|
+
? {
|
|
672
|
+
type: "label",
|
|
673
|
+
content: "A",
|
|
674
|
+
position: { x: 25, y: 25 },
|
|
675
|
+
action: () => { },
|
|
676
|
+
}
|
|
677
|
+
: undefined,
|
|
678
|
+
// @ts-expect-error TS2322
|
|
679
|
+
(props) => props.showB
|
|
680
|
+
? {
|
|
681
|
+
type: "label",
|
|
682
|
+
content: "B",
|
|
683
|
+
position: { x: 75, y: 75 },
|
|
684
|
+
action: () => { },
|
|
685
|
+
}
|
|
686
|
+
: undefined,
|
|
687
|
+
],
|
|
688
|
+
};
|
|
689
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
690
|
+
const resultBoth = map.display({ showA: true, showB: true });
|
|
691
|
+
expect(resultBoth.hotspots).toHaveLength(2);
|
|
692
|
+
const resultA = map.display({ showA: true, showB: false });
|
|
693
|
+
expect(resultA.hotspots).toHaveLength(1);
|
|
694
|
+
expect(resultA.hotspots[0].content).toBe("A");
|
|
695
|
+
const resultNone = map.display({ showA: false, showB: false });
|
|
696
|
+
expect(resultNone.hotspots).toHaveLength(0);
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
describe("Mixed Hotspots", () => {
|
|
700
|
+
test("displays map with multiple hotspot types", () => {
|
|
701
|
+
const options = {
|
|
702
|
+
image: "/map.jpg",
|
|
703
|
+
hotspots: [
|
|
704
|
+
{
|
|
705
|
+
type: "label",
|
|
706
|
+
content: "Village",
|
|
707
|
+
position: { x: 30, y: 40 },
|
|
708
|
+
action: () => { },
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
type: "image",
|
|
712
|
+
content: { idle: "/treasure.png" },
|
|
713
|
+
position: { x: 60, y: 70 },
|
|
714
|
+
action: () => { },
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
type: "label",
|
|
718
|
+
content: "Menu",
|
|
719
|
+
position: "top",
|
|
720
|
+
action: () => { },
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
type: "image",
|
|
724
|
+
content: { idle: "/compass.png" },
|
|
725
|
+
position: "bottom",
|
|
726
|
+
action: () => { },
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
type: "menu",
|
|
730
|
+
position: { x: 50, y: 50 },
|
|
731
|
+
items: [
|
|
732
|
+
{
|
|
733
|
+
type: "label",
|
|
734
|
+
content: "Option",
|
|
735
|
+
action: () => { },
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
},
|
|
739
|
+
],
|
|
740
|
+
};
|
|
741
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
742
|
+
const result = map.display();
|
|
743
|
+
expect(result.hotspots).toHaveLength(5);
|
|
744
|
+
expect(result.hotspots[0]?.type).toBe("label");
|
|
745
|
+
expect(result.hotspots[1]?.type).toBe("image");
|
|
746
|
+
expect(result.hotspots[2]?.type).toBe("label");
|
|
747
|
+
expect(result.hotspots[3]?.type).toBe("image");
|
|
748
|
+
expect(result.hotspots[4]?.type).toBe("menu");
|
|
749
|
+
});
|
|
750
|
+
test("mixed static and dynamic hotspots", () => {
|
|
751
|
+
const options = {
|
|
752
|
+
image: "/map.jpg",
|
|
753
|
+
hotspots: [
|
|
754
|
+
{
|
|
755
|
+
type: "label",
|
|
756
|
+
content: "Static",
|
|
757
|
+
position: { x: 25, y: 25 },
|
|
758
|
+
action: () => { },
|
|
759
|
+
},
|
|
760
|
+
() => ({
|
|
761
|
+
type: "label",
|
|
762
|
+
content: "Dynamic",
|
|
763
|
+
position: { x: 75, y: 75 },
|
|
764
|
+
action: () => { },
|
|
765
|
+
}),
|
|
766
|
+
],
|
|
767
|
+
};
|
|
768
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
769
|
+
const result = map.display();
|
|
770
|
+
expect(result.hotspots).toHaveLength(2);
|
|
771
|
+
expect(result.hotspots[0].content).toBe("Static");
|
|
772
|
+
expect(result.hotspots[1].content).toBe("Dynamic");
|
|
773
|
+
});
|
|
774
|
+
});
|
|
775
|
+
describe("Integration with Game", () => {
|
|
776
|
+
test("map is registered with Game on creation", () => {
|
|
777
|
+
const id = uniqueId("map");
|
|
778
|
+
const options = {
|
|
779
|
+
image: "/map.jpg",
|
|
780
|
+
hotspots: [],
|
|
781
|
+
};
|
|
782
|
+
newInteractiveMap(id, options);
|
|
783
|
+
const retrieved = Game.getPassageById(id);
|
|
784
|
+
expect(retrieved).not.toBeNull();
|
|
785
|
+
expect(retrieved?.id).toBe(id);
|
|
786
|
+
expect(retrieved?.type).toBe("interactiveMap");
|
|
787
|
+
});
|
|
788
|
+
test("can navigate to map using Game.jumpTo", () => {
|
|
789
|
+
const id = uniqueId("map");
|
|
790
|
+
const options = {
|
|
791
|
+
image: "/map.jpg",
|
|
792
|
+
hotspots: [],
|
|
793
|
+
};
|
|
794
|
+
newInteractiveMap(id, options);
|
|
795
|
+
Game.jumpTo(id);
|
|
796
|
+
expect(Game.selfState.currentPassageId).toBe(id);
|
|
797
|
+
});
|
|
798
|
+
test("can retrieve and display current map", () => {
|
|
799
|
+
const id = uniqueId("map");
|
|
800
|
+
const options = {
|
|
801
|
+
caption: "Current Map",
|
|
802
|
+
image: "/map.jpg",
|
|
803
|
+
hotspots: [
|
|
804
|
+
{
|
|
805
|
+
type: "label",
|
|
806
|
+
content: "Test",
|
|
807
|
+
position: { x: 50, y: 50 },
|
|
808
|
+
action: () => { },
|
|
809
|
+
},
|
|
810
|
+
],
|
|
811
|
+
};
|
|
812
|
+
newInteractiveMap(id, options);
|
|
813
|
+
Game.jumpTo(id);
|
|
814
|
+
const currentPassage = Game.currentPassage;
|
|
815
|
+
expect(currentPassage).not.toBeNull();
|
|
816
|
+
expect(currentPassage?.type).toBe("interactiveMap");
|
|
817
|
+
const result = currentPassage.display();
|
|
818
|
+
expect(result.caption).toBe("Current Map");
|
|
819
|
+
expect(result.hotspots).toHaveLength(1);
|
|
820
|
+
});
|
|
821
|
+
test("hotspot actions can trigger navigation", () => {
|
|
822
|
+
const mapId = uniqueId("map");
|
|
823
|
+
const targetId = uniqueId("target");
|
|
824
|
+
// Create target passage
|
|
825
|
+
const targetOptions = {
|
|
826
|
+
image: "/target.jpg",
|
|
827
|
+
hotspots: [],
|
|
828
|
+
};
|
|
829
|
+
newInteractiveMap(targetId, targetOptions);
|
|
830
|
+
// Create map with hotspot that navigates
|
|
831
|
+
let actionCalled = false;
|
|
832
|
+
const mapOptions = {
|
|
833
|
+
image: "/map.jpg",
|
|
834
|
+
hotspots: [
|
|
835
|
+
{
|
|
836
|
+
type: "label",
|
|
837
|
+
content: "Go to target",
|
|
838
|
+
position: { x: 50, y: 50 },
|
|
839
|
+
action: () => {
|
|
840
|
+
actionCalled = true;
|
|
841
|
+
Game.jumpTo(targetId);
|
|
842
|
+
},
|
|
843
|
+
},
|
|
844
|
+
],
|
|
845
|
+
};
|
|
846
|
+
newInteractiveMap(mapId, mapOptions);
|
|
847
|
+
Game.jumpTo(mapId);
|
|
848
|
+
const map = Game.currentPassage;
|
|
849
|
+
const result = map.display();
|
|
850
|
+
// Simulate clicking the hotspot
|
|
851
|
+
result.hotspots[0].action();
|
|
852
|
+
expect(actionCalled).toBe(true);
|
|
853
|
+
expect(Game.selfState.currentPassageId).toBe(targetId);
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
describe("Edge Cases", () => {
|
|
857
|
+
test("empty hotspots array", () => {
|
|
858
|
+
const options = {
|
|
859
|
+
image: "/map.jpg",
|
|
860
|
+
hotspots: [],
|
|
861
|
+
};
|
|
862
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
863
|
+
const result = map.display();
|
|
864
|
+
expect(result.hotspots).toHaveLength(0);
|
|
865
|
+
});
|
|
866
|
+
test("all hotspots return undefined", () => {
|
|
867
|
+
const options = {
|
|
868
|
+
image: "/map.jpg",
|
|
869
|
+
hotspots: [() => undefined, () => undefined, () => undefined],
|
|
870
|
+
};
|
|
871
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
872
|
+
const result = map.display();
|
|
873
|
+
expect(result.hotspots).toHaveLength(0);
|
|
874
|
+
});
|
|
875
|
+
test("display called multiple times returns different results", () => {
|
|
876
|
+
let callCount = 0;
|
|
877
|
+
const options = {
|
|
878
|
+
image: () => `/map-${++callCount}.jpg`,
|
|
879
|
+
hotspots: [],
|
|
880
|
+
};
|
|
881
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
882
|
+
const result1 = map.display();
|
|
883
|
+
expect(result1.image).toBe("/map-1.jpg");
|
|
884
|
+
const result2 = map.display();
|
|
885
|
+
expect(result2.image).toBe("/map-2.jpg");
|
|
886
|
+
});
|
|
887
|
+
test("hotspot with all button color variants", () => {
|
|
888
|
+
const colors = ["default", "primary", "secondary", "success", "warning", "danger"];
|
|
889
|
+
const options = {
|
|
890
|
+
image: "/map.jpg",
|
|
891
|
+
hotspots: colors.map((color, i) => ({
|
|
892
|
+
type: "label",
|
|
893
|
+
content: color,
|
|
894
|
+
position: { x: i * 10, y: i * 10 },
|
|
895
|
+
action: () => { },
|
|
896
|
+
props: { color },
|
|
897
|
+
})),
|
|
898
|
+
};
|
|
899
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
900
|
+
const result = map.display();
|
|
901
|
+
expect(result.hotspots).toHaveLength(6);
|
|
902
|
+
colors.forEach((color, i) => {
|
|
903
|
+
expect(result.hotspots[i].props?.color).toBe(color);
|
|
904
|
+
});
|
|
905
|
+
});
|
|
906
|
+
test("hotspot with all button variants", () => {
|
|
907
|
+
const variants = ["solid", "bordered", "light", "flat", "faded", "shadow", "ghost"];
|
|
908
|
+
const options = {
|
|
909
|
+
image: "/map.jpg",
|
|
910
|
+
hotspots: variants.map((variant, i) => ({
|
|
911
|
+
type: "label",
|
|
912
|
+
content: variant,
|
|
913
|
+
position: { x: i * 10, y: i * 10 },
|
|
914
|
+
action: () => { },
|
|
915
|
+
props: { variant },
|
|
916
|
+
})),
|
|
917
|
+
};
|
|
918
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
919
|
+
const result = map.display();
|
|
920
|
+
expect(result.hotspots).toHaveLength(7);
|
|
921
|
+
variants.forEach((variant, i) => {
|
|
922
|
+
expect(result.hotspots[i].props?.variant).toBe(variant);
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
test("image hotspot with different zoom values", () => {
|
|
926
|
+
const zooms = ["50%", "100%", "150%", "200%"];
|
|
927
|
+
const options = {
|
|
928
|
+
image: "/map.jpg",
|
|
929
|
+
hotspots: zooms.map((zoom, i) => ({
|
|
930
|
+
type: "image",
|
|
931
|
+
content: { idle: "/icon.png" },
|
|
932
|
+
position: { x: i * 20, y: i * 20 },
|
|
933
|
+
action: () => { },
|
|
934
|
+
props: { zoom },
|
|
935
|
+
})),
|
|
936
|
+
};
|
|
937
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
938
|
+
const result = map.display();
|
|
939
|
+
expect(result.hotspots).toHaveLength(4);
|
|
940
|
+
zooms.forEach((zoom, i) => {
|
|
941
|
+
expect(result.hotspots[i].props?.zoom).toBe(zoom);
|
|
942
|
+
});
|
|
943
|
+
});
|
|
944
|
+
test("menu with empty items array", () => {
|
|
945
|
+
const menu = {
|
|
946
|
+
type: "menu",
|
|
947
|
+
position: { x: 50, y: 50 },
|
|
948
|
+
items: [],
|
|
949
|
+
};
|
|
950
|
+
const options = {
|
|
951
|
+
image: "/map.jpg",
|
|
952
|
+
hotspots: [menu],
|
|
953
|
+
};
|
|
954
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
955
|
+
const result = map.display();
|
|
956
|
+
expect(result.hotspots[0].items).toHaveLength(0);
|
|
957
|
+
});
|
|
958
|
+
test("position with edge values (0, 100)", () => {
|
|
959
|
+
const options = {
|
|
960
|
+
image: "/map.jpg",
|
|
961
|
+
hotspots: [
|
|
962
|
+
{
|
|
963
|
+
type: "label",
|
|
964
|
+
content: "Top-Left",
|
|
965
|
+
position: { x: 0, y: 0 },
|
|
966
|
+
action: () => { },
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
type: "label",
|
|
970
|
+
content: "Bottom-Right",
|
|
971
|
+
position: { x: 100, y: 100 },
|
|
972
|
+
action: () => { },
|
|
973
|
+
},
|
|
974
|
+
],
|
|
975
|
+
};
|
|
976
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
977
|
+
const result = map.display();
|
|
978
|
+
expect(result.hotspots[0].position).toEqual({
|
|
979
|
+
x: 0,
|
|
980
|
+
y: 0,
|
|
981
|
+
});
|
|
982
|
+
expect(result.hotspots[1].position).toEqual({
|
|
983
|
+
x: 100,
|
|
984
|
+
y: 100,
|
|
985
|
+
});
|
|
986
|
+
});
|
|
987
|
+
test("bgOpacity with different values", () => {
|
|
988
|
+
const opacities = [0, 0.25, 0.5, 0.75, 1];
|
|
989
|
+
opacities.forEach((bgOpacity) => {
|
|
990
|
+
const options = {
|
|
991
|
+
image: "/map.jpg",
|
|
992
|
+
bgImage: "/bg.jpg",
|
|
993
|
+
hotspots: [],
|
|
994
|
+
props: { bgOpacity },
|
|
995
|
+
};
|
|
996
|
+
const map = newInteractiveMap(uniqueId("map"), options);
|
|
997
|
+
const result = map.display();
|
|
998
|
+
expect(result.props?.bgOpacity).toBe(bgOpacity);
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
//# sourceMappingURL=interactiveMap.test.js.map
|