@supermousejs/core 2.4.1 → 2.4.2

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.
@@ -1,291 +0,0 @@
1
- import { describe, it, expect, afterEach, vi } from "vitest";
2
- import { Supermouse } from "../Supermouse";
3
- import { DEFAULT_HOVER_SELECTORS } from "../Supermouse";
4
-
5
- describe("Supermouse core", () => {
6
- let app: Supermouse;
7
-
8
- afterEach(() => {
9
- app?.destroy();
10
- document.body.innerHTML = "";
11
- document.head.innerHTML = "";
12
- vi.restoreAllMocks();
13
- });
14
-
15
- it("registers and looks up plugins by name", () => {
16
- const app = new Supermouse();
17
-
18
- const plugin = {
19
- name: "test-plugin",
20
- install() {},
21
- update() {}
22
- };
23
-
24
- app.use(plugin);
25
-
26
- expect(app.getPlugin("test-plugin")).toBe(plugin);
27
- expect(app.getPlugin("missing-plugin")).toBeUndefined();
28
- });
29
-
30
- it("applies default options", () => {
31
- app = new Supermouse({ autoStart: false });
32
- expect(app.options.smoothness).toBe(0.15);
33
- expect(app.options.enableTouch).toBe(false);
34
- expect(app.options.autoDisableOnMobile).toBe(true);
35
- expect(app.options.cursor).toBe("auto");
36
- expect(app.options.hideOnLeave).toBe(true);
37
- expect(app.options.autoStart).toBe(false);
38
- expect(app.options.container).toBe(document.body);
39
- expect(app.options.dataPrefix).toBe("supermouse");
40
- expect(app.options.zIndex).toBe(9999);
41
- expect((app.options as any).ignoreOnNative).toBeUndefined();
42
- });
43
-
44
- it("merges user options over defaults", () => {
45
- const container = document.createElement("div");
46
- document.body.appendChild(container);
47
-
48
- app = new Supermouse({
49
- smoothness: 0.3,
50
- enableTouch: true,
51
- autoDisableOnMobile: false,
52
- cursor: "custom",
53
- hideOnLeave: false,
54
- autoStart: false,
55
- container,
56
- dataPrefix: "custom",
57
- zIndex: 1234
58
- });
59
-
60
- expect(app.options.smoothness).toBe(0.3);
61
- expect(app.options.enableTouch).toBe(true);
62
- expect(app.options.autoDisableOnMobile).toBe(false);
63
- expect(app.options.cursor).toBe("custom");
64
- expect(app.options.hideOnLeave).toBe(false);
65
- expect(app.options.autoStart).toBe(false);
66
- expect(app.options.container).toBe(container);
67
- expect(app.options.dataPrefix).toBe("custom");
68
- expect(app.options.zIndex).toBe(1234);
69
- });
70
-
71
- it("initializes state with correct shape", () => {
72
- app = new Supermouse({ autoStart: false });
73
- const { state } = app;
74
- expect(state.pointer).toEqual({ x: -100, y: -100 });
75
- expect(state.target).toEqual({ x: -100, y: -100 });
76
- expect(state.smooth).toEqual({ x: -100, y: -100 });
77
- expect(state.velocity).toEqual({ x: 0, y: 0 });
78
- expect(state.displacement).toEqual({ x: 0, y: 0 });
79
- expect(state.angle).toBe(0);
80
- expect(state.isDown).toBe(false);
81
- expect(state.isHover).toBe(false);
82
- expect(state.isNative).toBe(false);
83
- expect(state.cursorMode).toBe("auto");
84
- expect(state.hoverTarget).toBeNull();
85
- expect(state.reducedMotion).toBe(false);
86
- expect(state.hasReceivedInput).toBe(false);
87
- expect(state.shape).toBeNull();
88
- expect(state.interaction).toEqual({});
89
- });
90
-
91
- it("appends stage element to container (default body)", () => {
92
- app = new Supermouse({ autoStart: false });
93
- const stage = app.stage;
94
- expect(stage).toBeInstanceOf(HTMLDivElement);
95
- expect(document.body.contains(stage)).toBe(true);
96
- });
97
-
98
- it("appends stage element to custom container", () => {
99
- const container = document.createElement("div");
100
- document.body.appendChild(container);
101
- app = new Supermouse({ container, autoStart: false });
102
- expect(container.contains(app.stage)).toBe(true);
103
- });
104
-
105
- it("exposes version", () => {
106
- app = new Supermouse({ autoStart: false });
107
- expect(app.version).toBeDefined();
108
- expect(typeof app.version).toBe("string");
109
- });
110
-
111
- it("sets default hover selectors", () => {
112
- app = new Supermouse({ autoStart: false });
113
- const selectors = Array.from((app as any).hoverSelectors);
114
- expect(selectors).toEqual(expect.arrayContaining(DEFAULT_HOVER_SELECTORS));
115
- });
116
-
117
- it("overrides hover selectors if provided", () => {
118
- app = new Supermouse({
119
- autoStart: false,
120
- hoverSelectors: [".my-custom-hover", "a"]
121
- });
122
- const selectors = Array.from((app as any).hoverSelectors);
123
- expect(selectors).toEqual([".my-custom-hover", "a"]);
124
- });
125
-
126
- it("auto-starts the loop if autoStart is true", () => {
127
- const spy = vi.spyOn(Supermouse.prototype as any, "startLoop");
128
- app = new Supermouse({ autoStart: true });
129
- expect(spy).toHaveBeenCalled();
130
- spy.mockRestore();
131
- });
132
-
133
- it("setCursor updates cursorMode", () => {
134
- app = new Supermouse({ autoStart: false });
135
- app.setCursor("native");
136
- expect(app.state.cursorMode).toBe("native");
137
- app.setCursor("custom");
138
- expect(app.state.cursorMode).toBe("custom");
139
- app.setCursor("auto");
140
- expect(app.state.cursorMode).toBe("auto");
141
- });
142
-
143
- it("cursor option sets initial cursorMode", () => {
144
- app = new Supermouse({ autoStart: false, cursor: "native" });
145
- expect(app.state.cursorMode).toBe("native");
146
- });
147
-
148
- it("reads data attributes into interaction on hover", () => {
149
- app = new Supermouse({ autoStart: false });
150
- const div = document.createElement("div");
151
- div.setAttribute("data-supermouse-test", "hello");
152
- div.setAttribute("data-supermouse-flag", "");
153
- div.setAttribute("data-cursor", "");
154
- document.body.appendChild(div);
155
-
156
- div.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
157
-
158
- expect(app.state.isHover).toBe(true);
159
- expect(app.state.hoverTarget).toBe(div);
160
- expect(app.state.interaction).toEqual({ test: "hello", flag: true });
161
- });
162
-
163
- it("evaluates rules on hover", () => {
164
- app = new Supermouse({
165
- autoStart: false,
166
- rules: {
167
- "[data-cursor]": { custom: "rule-value" }
168
- }
169
- });
170
- const div = document.createElement("div");
171
- div.setAttribute("data-cursor", "");
172
- document.body.appendChild(div);
173
-
174
- div.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
175
-
176
- expect(app.state.interaction).toEqual({ custom: "rule-value" });
177
- });
178
-
179
- it("caches selector matching for rules", () => {
180
- app = new Supermouse({
181
- autoStart: false,
182
- rules: {
183
- "[data-cursor]": { test: "value" }
184
- }
185
- });
186
- const div = document.createElement("div");
187
- div.setAttribute("data-cursor", "");
188
- document.body.appendChild(div);
189
- div.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
190
-
191
- const input = (app as any).input;
192
- const spy = vi.spyOn(input, "matchesSelector");
193
-
194
- // Same element again – selector matching should be cached
195
- input.parseDOMInteraction(div);
196
- expect(spy).not.toHaveBeenCalled();
197
-
198
- // Different element – should trigger selector matching
199
- const div2 = document.createElement("div");
200
- div2.setAttribute("data-cursor", "");
201
- document.body.appendChild(div2);
202
- input.parseDOMInteraction(div2);
203
- expect(spy).toHaveBeenCalled();
204
- });
205
-
206
- it("re-enable restores original cursor mode after disable", () => {
207
- app = new Supermouse({ cursor: "custom", autoStart: false });
208
- expect(app.state.cursorMode).toBe("custom");
209
-
210
- app.disable();
211
- expect(app.state.cursorMode).toBe("custom");
212
-
213
- app.enable();
214
- expect(app.state.cursorMode).toBe("custom");
215
- });
216
-
217
- it("suspend/resume does not change cursor mode", () => {
218
- app = new Supermouse({ cursor: "custom", autoStart: false });
219
- app.start();
220
- app.suspend();
221
- expect(app.state.cursorMode).toBe("custom");
222
- app.resume();
223
- expect(app.state.cursorMode).toBe("custom");
224
- });
225
-
226
- it("hideOnLeave resets pointer and hasReceivedInput", () => {
227
- app = new Supermouse({ hideOnLeave: true, autoStart: false });
228
- window.dispatchEvent(
229
- new PointerEvent("pointermove", { clientX: 50, clientY: 60, pointerType: "mouse" })
230
- );
231
- expect(app.state.hasReceivedInput).toBe(true);
232
-
233
- document.dispatchEvent(new MouseEvent("mouseout", { relatedTarget: null }));
234
- expect(app.state.hasReceivedInput).toBe(false);
235
- expect(app.state.pointer).toEqual({ x: -100, y: -100 });
236
- });
237
-
238
- it("scoped instances maintain independent cursor modes", () => {
239
- const container1 = document.createElement("div");
240
- const container2 = document.createElement("div");
241
- document.body.appendChild(container1);
242
- document.body.appendChild(container2);
243
-
244
- const app1 = new Supermouse({ container: container1, cursor: "custom", autoStart: false });
245
- const app2 = new Supermouse({ container: container2, cursor: "native", autoStart: false });
246
-
247
- expect(app1.state.cursorMode).toBe("custom");
248
- expect(app2.state.cursorMode).toBe("native");
249
-
250
- app1.disable();
251
- expect(app1.state.cursorMode).toBe("custom");
252
- expect(app2.state.cursorMode).toBe("native"); // unaffected
253
-
254
- app1.destroy();
255
- app2.destroy();
256
- container1.remove();
257
- container2.remove();
258
- });
259
-
260
- it("matches simple selectors directly", () => {
261
- app = new Supermouse({
262
- autoStart: false,
263
- rules: {
264
- ".simple": { test: "ok" }
265
- }
266
- });
267
- const div = document.createElement("div");
268
- div.className = "simple";
269
- document.body.appendChild(div);
270
- div.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
271
- expect(app.state.interaction).toEqual({ test: "ok" });
272
- });
273
-
274
- it("matches complex selectors with ancestor", () => {
275
- app = new Supermouse({
276
- autoStart: false,
277
- rules: {
278
- ".ancestor .descendant": { custom: "value" }
279
- }
280
- });
281
- const ancestor = document.createElement("div");
282
- ancestor.className = "ancestor";
283
- const child = document.createElement("span");
284
- child.className = "descendant";
285
- ancestor.appendChild(child);
286
- document.body.appendChild(ancestor);
287
-
288
- child.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
289
- expect(app.state.interaction).toEqual({ custom: "value" });
290
- });
291
- });
@@ -1,109 +0,0 @@
1
- import { describe, it, expect, afterEach, vi } from "vitest";
2
- import { Supermouse } from "../Supermouse";
3
-
4
- describe("Supermouse cursor modes", () => {
5
- let app: Supermouse;
6
- let container: HTMLElement;
7
-
8
- afterEach(() => {
9
- app?.destroy();
10
- document.body.innerHTML = "";
11
- document.head.innerHTML = "";
12
- vi.restoreAllMocks();
13
- });
14
-
15
- const hasClassPrefix = (el: HTMLElement, prefix: string) =>
16
- Array.from(el.classList).some((c) => c.startsWith(prefix));
17
-
18
- function createApp(cursor: "auto" | "custom" | "native" | "both") {
19
- container = document.createElement("div");
20
- document.body.appendChild(container);
21
- app = new Supermouse({ container, cursor, autoStart: false });
22
- }
23
-
24
- function hoverElement(el: HTMLElement) {
25
- el.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
26
- // Trigger an update cycle so core applies cursor decisions
27
- app.step(performance.now() + 16);
28
- }
29
-
30
- function movePointer(x: number, y: number) {
31
- window.dispatchEvent(
32
- new PointerEvent("pointermove", { clientX: x, clientY: y, pointerType: "mouse" })
33
- );
34
- }
35
-
36
- it("auto mode marks native inputs as isNative and hides custom stage", () => {
37
- createApp("auto");
38
- const input = document.createElement("input");
39
- container.appendChild(input);
40
- movePointer(50, 50);
41
- hoverElement(input);
42
-
43
- expect(app.state.isNative).toBe(true);
44
- expect(app.stage.style.opacity).toBe("0");
45
- expect(container.style.cursor).toBe("");
46
- });
47
-
48
- it("custom mode ignores native detection and shows custom cursor over inputs", () => {
49
- createApp("custom");
50
- const input = document.createElement("input");
51
- container.appendChild(input);
52
- movePointer(50, 50);
53
- hoverElement(input);
54
-
55
- expect(app.state.isNative).toBe(false);
56
- expect(app.stage.style.opacity).toBe("1");
57
- expect(container.style.cursor).toBe("none");
58
- });
59
-
60
- it("both mode shows custom cursor and keeps native cursor visible over inputs", () => {
61
- createApp("both");
62
- const input = document.createElement("input");
63
- container.appendChild(input);
64
- movePointer(50, 50);
65
- hoverElement(input);
66
-
67
- expect(app.state.isNative).toBe(false);
68
- expect(app.stage.style.opacity).toBe("1");
69
- expect(container.style.cursor).toBe("");
70
- expect(hasClassPrefix(container, "supermouse-hide-")).toBe(false);
71
- });
72
-
73
- it("native mode hides custom stage and keeps native cursor over inputs", () => {
74
- createApp("native");
75
- const input = document.createElement("input");
76
- container.appendChild(input);
77
- movePointer(50, 50);
78
- hoverElement(input);
79
-
80
- expect(app.state.isNative).toBe(false);
81
- expect(app.stage.style.opacity).toBe("0");
82
- expect(container.style.cursor).toBe("");
83
- });
84
-
85
- it("auto mode respects data-supermouse-ignore attribute (still native)", () => {
86
- createApp("auto");
87
- const div = document.createElement("div");
88
- div.setAttribute("data-supermouse-ignore", "");
89
- container.appendChild(div);
90
- movePointer(50, 50);
91
- hoverElement(div);
92
-
93
- expect(app.state.isNative).toBe(true);
94
- });
95
-
96
- it("custom mode ignores data-supermouse-ignore and keeps custom", () => {
97
- createApp("custom");
98
- const div = document.createElement("div");
99
- div.setAttribute("data-supermouse-ignore", "");
100
- container.appendChild(div);
101
- movePointer(50, 50);
102
- hoverElement(div);
103
-
104
- expect(app.state.isNative).toBe(false);
105
- expect(app.stage.style.opacity).toBe("1");
106
- expect(container.style.cursor).toBe("none");
107
- expect(hasClassPrefix(container, "supermouse-hide-")).toBe(true);
108
- });
109
- });
@@ -1,261 +0,0 @@
1
- import { describe, it, expect, afterEach, vi, beforeEach } from "vitest";
2
- import { Supermouse } from "../Supermouse";
3
-
4
- describe("Supermouse input handling", () => {
5
- let app: Supermouse;
6
- let container: HTMLElement;
7
-
8
- beforeEach(() => {
9
- container = document.createElement("div");
10
- document.body.appendChild(container);
11
- // Ensure the container is not body for relative coordinate tests
12
- app = new Supermouse({ container, autoStart: false });
13
- });
14
-
15
- afterEach(() => {
16
- app?.destroy();
17
- document.body.innerHTML = "";
18
- document.head.innerHTML = "";
19
- vi.restoreAllMocks();
20
- });
21
-
22
- function dispatchPointerMove(x: number, y: number, pointerType = "mouse") {
23
- const event = new PointerEvent("pointermove", {
24
- clientX: x,
25
- clientY: y,
26
- pointerType,
27
- bubbles: true
28
- });
29
- window.dispatchEvent(event);
30
- }
31
-
32
- function dispatchPointerDown() {
33
- window.dispatchEvent(new PointerEvent("pointerdown", { pointerType: "mouse", bubbles: true }));
34
- }
35
-
36
- function dispatchPointerUp() {
37
- window.dispatchEvent(new PointerEvent("pointerup", { pointerType: "mouse", bubbles: true }));
38
- }
39
-
40
- function dispatchMouseOver(target: HTMLElement) {
41
- target.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
42
- }
43
-
44
- function dispatchMouseOut(target: HTMLElement, relatedTarget?: Node) {
45
- const event = new MouseEvent("mouseout", {
46
- bubbles: true,
47
- relatedTarget: relatedTarget ?? null
48
- });
49
- target.dispatchEvent(event);
50
- }
51
-
52
- it("updates pointer and sets hasReceivedInput on first move", () => {
53
- dispatchPointerMove(100, 150);
54
- expect(app.state.pointer).toEqual({ x: 100, y: 150 });
55
- expect(app.state.hasReceivedInput).toBe(true);
56
- });
57
-
58
- it("continues updating pointer on subsequent moves", () => {
59
- dispatchPointerMove(100, 150);
60
- dispatchPointerMove(200, 250);
61
- expect(app.state.pointer).toEqual({ x: 200, y: 250 });
62
- });
63
-
64
- it("ignores touch events when autoDisableOnMobile is true and enableTouch is false", () => {
65
- // Simulate a coarse pointer device by mocking matchMedia
66
- vi.spyOn(window, "matchMedia").mockImplementation(
67
- (query) =>
68
- ({
69
- matches: query === "(pointer: fine)" ? false : false,
70
- media: query,
71
- onchange: null,
72
- addListener: () => {},
73
- removeListener: () => {},
74
- addEventListener: () => {},
75
- removeEventListener: () => {},
76
- dispatchEvent: () => false
77
- }) as MediaQueryList
78
- );
79
-
80
- // Recreate app with the mock (need to destroy previous)
81
- app.destroy();
82
- app = new Supermouse({ container, autoStart: false });
83
-
84
- dispatchPointerMove(50, 60, "touch");
85
- // Touch should be ignored, so pointer remains offscreen
86
- expect(app.state.pointer).toEqual({ x: -100, y: -100 });
87
- expect(app.state.hasReceivedInput).toBe(false);
88
- });
89
-
90
- it("sets isDown on pointerdown and clears on pointerup", () => {
91
- dispatchPointerDown();
92
- expect(app.state.isDown).toBe(true);
93
- dispatchPointerUp();
94
- expect(app.state.isDown).toBe(false);
95
- });
96
-
97
- it("detects hover on registered selector and parses data attributes", () => {
98
- const link = document.createElement("a");
99
- link.setAttribute("data-supermouse-text", "Hello");
100
- link.setAttribute("data-supermouse-icon", "pointer");
101
- link.setAttribute("data-supermouse-magnetic", ""); // empty becomes true
102
- container.appendChild(link);
103
-
104
- dispatchMouseOver(link);
105
-
106
- expect(app.state.isHover).toBe(true);
107
- expect(app.state.hoverTarget).toBe(link);
108
- expect(app.state.interaction).toEqual({
109
- text: "Hello",
110
- icon: "pointer",
111
- magnetic: true
112
- });
113
- });
114
-
115
- it("clears hover state on mouseout", () => {
116
- const link = document.createElement("a");
117
- container.appendChild(link);
118
-
119
- dispatchMouseOver(link);
120
- expect(app.state.isHover).toBe(true);
121
-
122
- dispatchMouseOut(link);
123
- expect(app.state.isHover).toBe(false);
124
- expect(app.state.hoverTarget).toBeNull();
125
- expect(app.state.interaction).toEqual({});
126
- });
127
-
128
- it("sets isNative for input elements when ignoreOnNative is auto", () => {
129
- const input = document.createElement("input");
130
- container.appendChild(input);
131
-
132
- dispatchMouseOver(input);
133
-
134
- expect(app.state.isNative).toBe(true);
135
- });
136
-
137
- it("sets isNative for elements with custom CSS cursor", () => {
138
- const div = document.createElement("div");
139
- div.style.cursor = "text"; // not in SUPERMOUSE_CURSORS
140
- container.appendChild(div);
141
-
142
- dispatchMouseOver(div);
143
-
144
- expect(app.state.isNative).toBe(true);
145
- });
146
-
147
- it("does not set isNative for elements with default cursor", () => {
148
- const div = document.createElement("div");
149
- // default cursor is auto/default, which is in SUPERMOUSE_CURSORS
150
- container.appendChild(div);
151
-
152
- dispatchMouseOver(div);
153
-
154
- expect(app.state.isNative).toBe(false);
155
- });
156
-
157
- it("sets isNative when element has ignore attribute", () => {
158
- const div = document.createElement("div");
159
- div.setAttribute("data-supermouse-ignore", "");
160
- container.appendChild(div);
161
-
162
- dispatchMouseOver(div);
163
-
164
- expect(app.state.isNative).toBe(true);
165
- });
166
-
167
- it("does not set isHover when element does not match hover selector", () => {
168
- const span = document.createElement("span");
169
- container.appendChild(span);
170
-
171
- dispatchMouseOver(span);
172
-
173
- expect(app.state.isHover).toBe(false);
174
- expect(app.state.hoverTarget).toBeNull();
175
- });
176
-
177
- it("resets pointer to offscreen on document mouseleave when hideOnLeave is true", () => {
178
- dispatchPointerMove(100, 100);
179
- expect(app.state.pointer).toEqual({ x: 100, y: 100 });
180
-
181
- const leaveEvent = new MouseEvent("mouseout", { relatedTarget: null });
182
- document.dispatchEvent(leaveEvent);
183
-
184
- expect(app.state.hasReceivedInput).toBe(false);
185
- expect(app.state.pointer).toEqual({ x: -100, y: -100 });
186
- });
187
-
188
- it("does not reset pointer on mouseleave if hideOnLeave is false", () => {
189
- app.destroy();
190
- app = new Supermouse({ container, autoStart: false, hideOnLeave: false });
191
-
192
- dispatchPointerMove(100, 100);
193
- const leaveEvent = new MouseEvent("mouseout", { relatedTarget: null });
194
- document.dispatchEvent(leaveEvent);
195
-
196
- expect(app.state.hasReceivedInput).toBe(true);
197
- expect(app.state.pointer).toEqual({ x: 100, y: 100 });
198
- });
199
-
200
- it("makes coordinates relative to container", () => {
201
- const rect = {
202
- left: 200,
203
- top: 300,
204
- right: 500,
205
- bottom: 600,
206
- width: 300,
207
- height: 300,
208
- x: 200,
209
- y: 300,
210
- toJSON: () => {}
211
- };
212
- vi.spyOn(container, "getBoundingClientRect").mockReturnValue(rect as DOMRect);
213
-
214
- window.dispatchEvent(new Event("resize"));
215
-
216
- dispatchPointerMove(250, 350);
217
-
218
- expect(app.state.pointer).toEqual({ x: 50, y: 50 });
219
- });
220
-
221
- it("ignores all custom behavior inside data-supermouse-ignore area", () => {
222
- app = new Supermouse({ container, autoStart: false });
223
-
224
- // Create ignored wrapper
225
- const ignored = document.createElement("div");
226
- ignored.setAttribute("data-supermouse-ignore", "");
227
- const innerButton = document.createElement("button");
228
- innerButton.setAttribute("data-supermouse-text", "Hello");
229
- ignored.appendChild(innerButton);
230
- container.appendChild(ignored);
231
-
232
- // Spy on parseDOMInteraction to ensure it's not called
233
- const input = (app as any).input;
234
- const parseSpy = vi.spyOn(input, "parseDOMInteraction");
235
-
236
- // Hover the inner button (inside ignored area)
237
- innerButton.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
238
-
239
- // Expect no interaction parsing
240
- expect(parseSpy).not.toHaveBeenCalled();
241
- // Native cursor shown
242
- expect(app.state.isNative).toBe(true);
243
- // Hover state cleared
244
- expect(app.state.isHover).toBe(false);
245
- expect(app.state.hoverTarget).toBeNull();
246
- // Interaction data empty
247
- expect(app.state.interaction).toEqual({});
248
-
249
- // Now move to a normal element outside ignored area
250
- const normalButton = document.createElement("button");
251
- container.appendChild(normalButton);
252
- normalButton.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
253
-
254
- // Normal behavior should resume
255
- expect(app.state.isNative).toBe(false);
256
- expect(app.state.isHover).toBe(true);
257
- expect(app.state.hoverTarget).toBe(normalButton);
258
- // Interaction parsing should happen again
259
- expect(parseSpy).toHaveBeenCalled();
260
- });
261
- });