@supermousejs/core 2.4.0 → 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.
- package/CHANGELOG.md +13 -0
- package/README.md +334 -30
- package/dist/index.mjs +9 -2
- package/dist/index.umd.js +1 -1
- package/package.json +7 -1
- package/src/Supermouse.ts +0 -953
- package/src/__tests__/core.spec.ts +0 -291
- package/src/__tests__/cursor-mode.spec.ts +0 -109
- package/src/__tests__/input.spec.ts +0 -261
- package/src/__tests__/integration.spec.ts +0 -121
- package/src/__tests__/lifecycle.spec.ts +0 -199
- package/src/__tests__/nested-instance.spec.ts +0 -51
- package/src/__tests__/plugin.spec.ts +0 -130
- package/src/__tests__/setup.ts +0 -21
- package/src/__tests__/stage.spec.ts +0 -235
- package/src/__tests__/suspend-resume.spec.ts +0 -50
- package/src/index.ts +0 -2
- package/src/types.ts +0 -165
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
- package/vite.config.ts +0 -32
- package/vitest.config.ts +0 -10
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
|
-
import { Supermouse } from "../Supermouse";
|
|
3
|
-
|
|
4
|
-
describe("Supermouse integration", () => {
|
|
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
|
-
it("disable() resets plugin-set cursorMode and restores native cursor", () => {
|
|
16
|
-
container = document.createElement("div");
|
|
17
|
-
document.body.appendChild(container);
|
|
18
|
-
app = new Supermouse({ container, autoStart: false });
|
|
19
|
-
|
|
20
|
-
const smartIconLike = {
|
|
21
|
-
name: "fake-smart-icon",
|
|
22
|
-
install(instance: Supermouse) {
|
|
23
|
-
instance.setCursor("custom");
|
|
24
|
-
},
|
|
25
|
-
update() {}
|
|
26
|
-
};
|
|
27
|
-
app.use(smartIconLike);
|
|
28
|
-
|
|
29
|
-
expect(app.state.cursorMode).toBe("custom");
|
|
30
|
-
|
|
31
|
-
window.dispatchEvent(
|
|
32
|
-
new PointerEvent("pointermove", { clientX: 100, clientY: 100, pointerType: "mouse" })
|
|
33
|
-
);
|
|
34
|
-
app.start();
|
|
35
|
-
app.step(performance.now() + 16);
|
|
36
|
-
|
|
37
|
-
expect(app.stage.style.opacity).toBe("1"); // custom cursor visible
|
|
38
|
-
|
|
39
|
-
app.disable();
|
|
40
|
-
|
|
41
|
-
expect(app.state.cursorMode).toBe("custom");
|
|
42
|
-
expect(app.stage.style.opacity).toBe("0");
|
|
43
|
-
expect(container.style.cursor).toBe("");
|
|
44
|
-
|
|
45
|
-
app.step(performance.now() + 32);
|
|
46
|
-
expect(container.style.cursor).toBe("");
|
|
47
|
-
expect(app.stage.style.opacity).toBe("0");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("disable() also works when plugin registers extra hover targets", () => {
|
|
51
|
-
container = document.createElement("div");
|
|
52
|
-
document.body.appendChild(container);
|
|
53
|
-
app = new Supermouse({ container, autoStart: false });
|
|
54
|
-
|
|
55
|
-
const smartIconLike = {
|
|
56
|
-
name: "fake-smart-icon-2",
|
|
57
|
-
install(instance: Supermouse) {
|
|
58
|
-
instance.setCursor("custom");
|
|
59
|
-
instance.registerHoverTarget("p, h1, h2, h3, span");
|
|
60
|
-
},
|
|
61
|
-
update() {}
|
|
62
|
-
};
|
|
63
|
-
app.use(smartIconLike);
|
|
64
|
-
|
|
65
|
-
window.dispatchEvent(
|
|
66
|
-
new PointerEvent("pointermove", { clientX: 50, clientY: 50, pointerType: "mouse" })
|
|
67
|
-
);
|
|
68
|
-
app.start();
|
|
69
|
-
app.step(performance.now() + 16);
|
|
70
|
-
|
|
71
|
-
expect(app.state.cursorMode).toBe("custom");
|
|
72
|
-
|
|
73
|
-
app.disable();
|
|
74
|
-
|
|
75
|
-
expect(app.state.cursorMode).toBe("custom");
|
|
76
|
-
expect(app.stage.style.opacity).toBe("0");
|
|
77
|
-
expect(container.style.cursor).toBe("");
|
|
78
|
-
|
|
79
|
-
expect(container.classList.contains("supermouse-hide-0")).toBe(false);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("nested instance with cursor 'both' does not inherit global inline cursor:none", () => {
|
|
83
|
-
// Global instance on body
|
|
84
|
-
const globalApp = new Supermouse({
|
|
85
|
-
container: document.body,
|
|
86
|
-
cursor: "auto",
|
|
87
|
-
autoStart: false
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Create nested preview container
|
|
91
|
-
const previewContainer = document.createElement("div");
|
|
92
|
-
document.body.appendChild(previewContainer);
|
|
93
|
-
|
|
94
|
-
// Preview instance with cursor 'both'
|
|
95
|
-
const previewApp = new Supermouse({
|
|
96
|
-
container: previewContainer,
|
|
97
|
-
cursor: "both",
|
|
98
|
-
autoStart: false
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// Simulate entering preview: suspend global
|
|
102
|
-
globalApp.suspend();
|
|
103
|
-
|
|
104
|
-
// Global body must not have inline cursor:none
|
|
105
|
-
expect(document.body.style.cursor).toBe("");
|
|
106
|
-
expect(document.body.classList.contains("supermouse-hide-0")).toBe(false);
|
|
107
|
-
|
|
108
|
-
// Preview container must also not have inline cursor:none (its own 'both' mode shouldn't)
|
|
109
|
-
expect(previewContainer.style.cursor).toBe("");
|
|
110
|
-
|
|
111
|
-
// An empty div inside preview should inherit nothing from body
|
|
112
|
-
const emptyDiv = document.createElement("div");
|
|
113
|
-
previewContainer.appendChild(emptyDiv);
|
|
114
|
-
// In real browser, computed cursor would be 'auto', but we can at least check no inline
|
|
115
|
-
expect(emptyDiv.style.cursor).toBe("");
|
|
116
|
-
|
|
117
|
-
globalApp.destroy();
|
|
118
|
-
previewApp.destroy();
|
|
119
|
-
previewContainer.remove();
|
|
120
|
-
});
|
|
121
|
-
});
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach, beforeEach, vi } from "vitest";
|
|
2
|
-
import { Supermouse } from "../Supermouse";
|
|
3
|
-
|
|
4
|
-
describe("Supermouse lifecycle", () => {
|
|
5
|
-
let app: Supermouse;
|
|
6
|
-
let container: HTMLElement;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
container = document.createElement("div");
|
|
10
|
-
document.body.appendChild(container);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
app?.destroy();
|
|
15
|
-
document.body.innerHTML = "";
|
|
16
|
-
document.head.innerHTML = "";
|
|
17
|
-
vi.restoreAllMocks();
|
|
18
|
-
vi.useRealTimers();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
function movePointer(x: number, y: number) {
|
|
22
|
-
window.dispatchEvent(
|
|
23
|
-
new PointerEvent("pointermove", {
|
|
24
|
-
clientX: x,
|
|
25
|
-
clientY: y,
|
|
26
|
-
pointerType: "mouse",
|
|
27
|
-
bubbles: true
|
|
28
|
-
})
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const hasClassPrefix = (el: HTMLElement, prefix: string) =>
|
|
33
|
-
Array.from(el.classList).some((c) => c.startsWith(prefix));
|
|
34
|
-
|
|
35
|
-
it("auto-starts the loop when autoStart is true", () => {
|
|
36
|
-
const startLoopSpy = vi.spyOn(Supermouse.prototype as any, "startLoop");
|
|
37
|
-
app = new Supermouse({ autoStart: true, container });
|
|
38
|
-
expect(startLoopSpy).toHaveBeenCalledTimes(1);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("does not auto-start when autoStart is false", () => {
|
|
42
|
-
const startLoopSpy = vi.spyOn(Supermouse.prototype as any, "startLoop");
|
|
43
|
-
app = new Supermouse({ autoStart: false, container });
|
|
44
|
-
expect(startLoopSpy).not.toHaveBeenCalled();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("start() starts the loop and is idempotent", () => {
|
|
48
|
-
app = new Supermouse({ autoStart: false, container });
|
|
49
|
-
const rafSpy = vi.spyOn(window, "requestAnimationFrame").mockReturnValue(1);
|
|
50
|
-
app.start();
|
|
51
|
-
expect(rafSpy).toHaveBeenCalledTimes(1);
|
|
52
|
-
app.start();
|
|
53
|
-
expect(rafSpy).toHaveBeenCalledTimes(1);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("disable() disables input, restores native cursor, and resets physics", () => {
|
|
57
|
-
app = new Supermouse({ autoStart: false, container });
|
|
58
|
-
movePointer(50, 60);
|
|
59
|
-
app.start();
|
|
60
|
-
|
|
61
|
-
app.disable();
|
|
62
|
-
|
|
63
|
-
expect(app.input.isEnabled).toBe(false);
|
|
64
|
-
expect(container.style.cursor).toBe(""); // original (empty)
|
|
65
|
-
expect(app.state.target).toEqual({ x: -100, y: -100 });
|
|
66
|
-
expect(app.state.smooth).toEqual({ x: -100, y: -100 });
|
|
67
|
-
expect(app.state.velocity).toEqual({ x: 0, y: 0 });
|
|
68
|
-
expect(app.state.displacement).toEqual({ x: 0, y: 0 });
|
|
69
|
-
expect(app.state.hasReceivedInput).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("enable() re-enables input, snaps to current pointer, and hides native cursor", () => {
|
|
73
|
-
app = new Supermouse({ autoStart: false, container });
|
|
74
|
-
movePointer(200, 250);
|
|
75
|
-
app.start();
|
|
76
|
-
app.disable();
|
|
77
|
-
app.enable();
|
|
78
|
-
|
|
79
|
-
expect(app.input.isEnabled).toBe(true);
|
|
80
|
-
expect(app.state.hasReceivedInput).toBe(true);
|
|
81
|
-
expect(app.state.target).toEqual({ x: 200, y: 250 });
|
|
82
|
-
expect(app.state.smooth).toEqual({ x: 200, y: 250 });
|
|
83
|
-
|
|
84
|
-
expect(hasClassPrefix(container, "supermouse-hide-")).toBe(true);
|
|
85
|
-
expect(container.style.cursor).toBe("none");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("suspend() disables input, clears hover, hides stage", () => {
|
|
89
|
-
app = new Supermouse({ autoStart: false, container });
|
|
90
|
-
// Simulate hovering an element
|
|
91
|
-
const link = document.createElement("a");
|
|
92
|
-
container.appendChild(link);
|
|
93
|
-
link.dispatchEvent(new MouseEvent("mouseover", { bubbles: true }));
|
|
94
|
-
expect(app.state.isHover).toBe(true);
|
|
95
|
-
|
|
96
|
-
app.start();
|
|
97
|
-
app.suspend();
|
|
98
|
-
|
|
99
|
-
expect(app.input.isEnabled).toBe(false);
|
|
100
|
-
expect(app.state.isHover).toBe(false);
|
|
101
|
-
expect(app.state.hoverTarget).toBeNull();
|
|
102
|
-
expect(app.state.interaction).toEqual({});
|
|
103
|
-
expect(app.stage.style.opacity).toBe("0");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("resume() re-enables input, snaps physics, and shows stage", () => {
|
|
107
|
-
app = new Supermouse({ autoStart: false, container });
|
|
108
|
-
movePointer(300, 400);
|
|
109
|
-
app.start();
|
|
110
|
-
app.suspend();
|
|
111
|
-
|
|
112
|
-
const updateSpy = vi.fn();
|
|
113
|
-
app.use({ name: "test", update: updateSpy });
|
|
114
|
-
app.resume();
|
|
115
|
-
|
|
116
|
-
expect(app.input.isEnabled).toBe(true);
|
|
117
|
-
expect(app.state.target).toEqual({ x: 300, y: 400 });
|
|
118
|
-
expect(app.state.smooth).toEqual({ x: 300, y: 400 });
|
|
119
|
-
expect(app.stage.style.opacity).toBe("1");
|
|
120
|
-
expect(updateSpy).toHaveBeenCalledWith(app, 0);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("manual step advances physics and updates velocity/displacement", () => {
|
|
124
|
-
app = new Supermouse({ autoStart: false, container });
|
|
125
|
-
app.input.isEnabled = true;
|
|
126
|
-
app.state.hasReceivedInput = true;
|
|
127
|
-
app.state.pointer = { x: 100, y: 100 };
|
|
128
|
-
app.state.target = { x: 100, y: 100 };
|
|
129
|
-
app.state.smooth = { x: 0, y: 0 };
|
|
130
|
-
app.start();
|
|
131
|
-
|
|
132
|
-
const now = performance.now() + 16;
|
|
133
|
-
app.step(now);
|
|
134
|
-
|
|
135
|
-
expect(app.state.smooth.x).toBeGreaterThan(0);
|
|
136
|
-
expect(app.state.smooth.x).toBeLessThan(100);
|
|
137
|
-
expect(app.state.velocity.x).not.toBe(0);
|
|
138
|
-
expect(app.state.displacement.x).toBeCloseTo(100 - app.state.smooth.x);
|
|
139
|
-
expect(app.state.displacement.y).toBeCloseTo(100 - app.state.smooth.y);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("pauses loop on document hidden and resumes on visible", () => {
|
|
143
|
-
app = new Supermouse({ autoStart: false, container });
|
|
144
|
-
const cancelSpy = vi.spyOn(window, "cancelAnimationFrame").mockImplementation(() => {});
|
|
145
|
-
const rafSpy = vi.spyOn(window, "requestAnimationFrame").mockReturnValue(1);
|
|
146
|
-
app.start();
|
|
147
|
-
|
|
148
|
-
Object.defineProperty(document, "hidden", { value: true, configurable: true });
|
|
149
|
-
Object.defineProperty(document, "visibilityState", { value: "hidden", configurable: true });
|
|
150
|
-
document.dispatchEvent(new Event("visibilitychange"));
|
|
151
|
-
|
|
152
|
-
expect(cancelSpy).toHaveBeenCalled();
|
|
153
|
-
|
|
154
|
-
Object.defineProperty(document, "hidden", { value: false, configurable: true });
|
|
155
|
-
Object.defineProperty(document, "visibilityState", { value: "visible", configurable: true });
|
|
156
|
-
document.dispatchEvent(new Event("visibilitychange"));
|
|
157
|
-
|
|
158
|
-
expect(rafSpy).toHaveBeenCalledTimes(2); // initial + resume
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("destroy() stops loop, removes event listeners, stage, and plugins", () => {
|
|
162
|
-
const destroyPluginSpy = vi.fn();
|
|
163
|
-
|
|
164
|
-
app = new Supermouse({ autoStart: false, container });
|
|
165
|
-
const stageEl = app.stage;
|
|
166
|
-
const styleTag = document.querySelector('style[id^="supermouse-style-"]');
|
|
167
|
-
app.use({ name: "plugin", destroy: destroyPluginSpy });
|
|
168
|
-
|
|
169
|
-
app.destroy();
|
|
170
|
-
|
|
171
|
-
expect(app.isRunning).toBe(false);
|
|
172
|
-
expect(document.body.contains(stageEl)).toBe(false);
|
|
173
|
-
expect(document.head.contains(styleTag)).toBe(false);
|
|
174
|
-
expect(destroyPluginSpy).toHaveBeenCalled();
|
|
175
|
-
expect(container.style.cursor).toBe("");
|
|
176
|
-
expect(container.classList.contains("supermouse-scope")).toBe(false);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it("disable() hides the custom cursor stage", () => {
|
|
180
|
-
app = new Supermouse({ autoStart: false });
|
|
181
|
-
// Simulate pointer presence
|
|
182
|
-
window.dispatchEvent(
|
|
183
|
-
new PointerEvent("pointermove", { clientX: 100, clientY: 100, pointerType: "mouse" })
|
|
184
|
-
);
|
|
185
|
-
expect(app.stage.style.opacity).toBe("1");
|
|
186
|
-
|
|
187
|
-
app.disable();
|
|
188
|
-
|
|
189
|
-
expect(app.stage.style.opacity).toBe("0");
|
|
190
|
-
expect(container.style.cursor).toBe("");
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it("disable() preserves cursorMode", () => {
|
|
194
|
-
app = new Supermouse({ autoStart: false });
|
|
195
|
-
app.setCursor("custom");
|
|
196
|
-
app.disable();
|
|
197
|
-
expect(app.state.cursorMode).toBe("custom");
|
|
198
|
-
});
|
|
199
|
-
});
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
-
import { Supermouse } from "../Supermouse";
|
|
3
|
-
|
|
4
|
-
describe("Nested instances cursor inheritance", () => {
|
|
5
|
-
let globalApp: Supermouse;
|
|
6
|
-
let previewApp: Supermouse;
|
|
7
|
-
let body: HTMLElement;
|
|
8
|
-
let previewContainer: HTMLElement;
|
|
9
|
-
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
globalApp?.destroy();
|
|
12
|
-
previewApp?.destroy();
|
|
13
|
-
document.body.innerHTML = "";
|
|
14
|
-
document.head.innerHTML = "";
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("preview container should set inline cursor to auto when in 'both' mode to override inherited none", () => {
|
|
18
|
-
body = document.body;
|
|
19
|
-
globalApp = new Supermouse({ container: body, cursor: "auto", autoStart: false });
|
|
20
|
-
|
|
21
|
-
// simulate global instance hiding native cursor
|
|
22
|
-
globalApp.setCursor("custom"); // force custom -> native suppressed
|
|
23
|
-
globalApp.step(performance.now() + 16);
|
|
24
|
-
expect(body.style.cursor).toBe("none");
|
|
25
|
-
|
|
26
|
-
previewContainer = document.createElement("div");
|
|
27
|
-
body.appendChild(previewContainer);
|
|
28
|
-
|
|
29
|
-
previewApp = new Supermouse({ container: previewContainer, cursor: "both", autoStart: false });
|
|
30
|
-
previewApp.step(performance.now() + 16);
|
|
31
|
-
|
|
32
|
-
// The preview container should have its own inline cursor set to 'auto'
|
|
33
|
-
// to override the inherited 'none' from body.
|
|
34
|
-
expect(previewContainer.style.cursor).toBe("auto");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("nested both mode should show native cursor (KNOWN ISSUE)", () => {
|
|
38
|
-
const global = new Supermouse({ container: document.body, cursor: "auto", autoStart: false });
|
|
39
|
-
global.setCursor("custom");
|
|
40
|
-
global.step(performance.now() + 16);
|
|
41
|
-
expect(document.body.style.cursor).toBe("none");
|
|
42
|
-
|
|
43
|
-
const preview = document.createElement("div");
|
|
44
|
-
document.body.appendChild(preview);
|
|
45
|
-
const local = new Supermouse({ container: preview, cursor: "both", autoStart: false });
|
|
46
|
-
local.step(performance.now() + 16);
|
|
47
|
-
|
|
48
|
-
// This will fail with current stable code – intentional.
|
|
49
|
-
expect(preview.style.cursor).toBe("auto");
|
|
50
|
-
});
|
|
51
|
-
});
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach, vi } from "vitest";
|
|
2
|
-
import { Supermouse } from "../Supermouse";
|
|
3
|
-
|
|
4
|
-
describe("Supermouse plugin system", () => {
|
|
5
|
-
let app: Supermouse;
|
|
6
|
-
|
|
7
|
-
afterEach(() => {
|
|
8
|
-
app?.destroy();
|
|
9
|
-
document.body.innerHTML = "";
|
|
10
|
-
document.head.innerHTML = "";
|
|
11
|
-
vi.restoreAllMocks();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("registers and looks up plugins by name", () => {
|
|
15
|
-
app = new Supermouse({ autoStart: false });
|
|
16
|
-
const plugin = { name: "test-plugin", install() {}, update() {} };
|
|
17
|
-
app.use(plugin);
|
|
18
|
-
expect(app.getPlugin("test-plugin")).toBe(plugin);
|
|
19
|
-
expect(app.getPlugin("missing-plugin")).toBeUndefined();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("calls install hook when plugin is added", () => {
|
|
23
|
-
const install = vi.fn();
|
|
24
|
-
app = new Supermouse({ autoStart: false });
|
|
25
|
-
app.use({ name: "plugin", install });
|
|
26
|
-
expect(install).toHaveBeenCalledWith(app);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("prevents duplicate plugin names and logs warning", () => {
|
|
30
|
-
const warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
31
|
-
app = new Supermouse({ autoStart: false });
|
|
32
|
-
const plugin = { name: "dupe", update() {} };
|
|
33
|
-
app.use(plugin);
|
|
34
|
-
app.use(plugin);
|
|
35
|
-
expect(warn).toHaveBeenCalled();
|
|
36
|
-
expect(app.plugins?.length).toBe(1); // private, but accessible in test
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("sorts plugins by priority (lower runs first)", async () => {
|
|
40
|
-
app = new Supermouse({ autoStart: false });
|
|
41
|
-
const order: number[] = [];
|
|
42
|
-
app.use({
|
|
43
|
-
name: "low",
|
|
44
|
-
priority: 10,
|
|
45
|
-
update: () => order.push(10)
|
|
46
|
-
});
|
|
47
|
-
app.use({
|
|
48
|
-
name: "high",
|
|
49
|
-
priority: 1,
|
|
50
|
-
update: () => order.push(1)
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Manually step once (simulate a frame)
|
|
54
|
-
app.start();
|
|
55
|
-
app.step(performance.now() + 16);
|
|
56
|
-
app.destroy(); // stop the loop
|
|
57
|
-
|
|
58
|
-
expect(order).toEqual([1, 10]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("enables and disables plugins via methods", () => {
|
|
62
|
-
app = new Supermouse({ autoStart: false });
|
|
63
|
-
const onEnable = vi.fn();
|
|
64
|
-
const onDisable = vi.fn();
|
|
65
|
-
const plugin = {
|
|
66
|
-
name: "toggle-plugin",
|
|
67
|
-
isEnabled: true,
|
|
68
|
-
onEnable,
|
|
69
|
-
onDisable,
|
|
70
|
-
element: document.createElement("div")
|
|
71
|
-
};
|
|
72
|
-
app.use(plugin);
|
|
73
|
-
|
|
74
|
-
app.disablePlugin("toggle-plugin");
|
|
75
|
-
expect(plugin.isEnabled).toBe(false);
|
|
76
|
-
expect(onDisable).toHaveBeenCalledWith(app);
|
|
77
|
-
expect(plugin.element.style.display).toBe("none");
|
|
78
|
-
|
|
79
|
-
app.enablePlugin("toggle-plugin");
|
|
80
|
-
expect(plugin.isEnabled).toBe(true);
|
|
81
|
-
expect(onEnable).toHaveBeenCalledWith(app);
|
|
82
|
-
expect(plugin.element.style.display).toBe("");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("toggles plugin state", () => {
|
|
86
|
-
app = new Supermouse({ autoStart: false });
|
|
87
|
-
const plugin = { name: "toggle", isEnabled: true };
|
|
88
|
-
app.use(plugin);
|
|
89
|
-
app.togglePlugin("toggle");
|
|
90
|
-
expect(plugin.isEnabled).toBe(false);
|
|
91
|
-
app.togglePlugin("toggle");
|
|
92
|
-
expect(plugin.isEnabled).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("disables and cleans up crashed plugins", () => {
|
|
96
|
-
app = new Supermouse({ autoStart: false });
|
|
97
|
-
const error = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
98
|
-
const plugin = {
|
|
99
|
-
name: "crasher",
|
|
100
|
-
isEnabled: true,
|
|
101
|
-
update: () => {
|
|
102
|
-
throw new Error("boom");
|
|
103
|
-
},
|
|
104
|
-
element: document.createElement("div"),
|
|
105
|
-
onDisable: vi.fn(),
|
|
106
|
-
destroy: vi.fn()
|
|
107
|
-
};
|
|
108
|
-
app.use(plugin);
|
|
109
|
-
|
|
110
|
-
app.start();
|
|
111
|
-
app.step(performance.now() + 16);
|
|
112
|
-
app.destroy();
|
|
113
|
-
|
|
114
|
-
expect(error).toHaveBeenCalled();
|
|
115
|
-
expect(plugin.isEnabled).toBe(false);
|
|
116
|
-
expect(plugin.onDisable).toHaveBeenCalled();
|
|
117
|
-
expect(plugin.destroy).toHaveBeenCalled();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("calls destroy hooks on all plugins when instance is destroyed", () => {
|
|
121
|
-
app = new Supermouse({ autoStart: false });
|
|
122
|
-
const destroy1 = vi.fn();
|
|
123
|
-
const destroy2 = vi.fn();
|
|
124
|
-
app.use({ name: "p1", destroy: destroy1 });
|
|
125
|
-
app.use({ name: "p2", destroy: destroy2 });
|
|
126
|
-
app.destroy();
|
|
127
|
-
expect(destroy1).toHaveBeenCalled();
|
|
128
|
-
expect(destroy2).toHaveBeenCalled();
|
|
129
|
-
});
|
|
130
|
-
});
|
package/src/__tests__/setup.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
window.matchMedia = (query: string) => {
|
|
2
|
-
return {
|
|
3
|
-
matches: query === "(pointer: fine)",
|
|
4
|
-
media: query,
|
|
5
|
-
onchange: null,
|
|
6
|
-
addListener: () => {},
|
|
7
|
-
removeListener: () => {},
|
|
8
|
-
addEventListener: () => {},
|
|
9
|
-
removeEventListener: () => {},
|
|
10
|
-
dispatchEvent: () => false
|
|
11
|
-
} as MediaQueryList;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
if (!window.ResizeObserver) {
|
|
15
|
-
class ResizeObserverMock {
|
|
16
|
-
observe() {}
|
|
17
|
-
unobserve() {}
|
|
18
|
-
disconnect() {}
|
|
19
|
-
}
|
|
20
|
-
window.ResizeObserver = ResizeObserverMock as any;
|
|
21
|
-
}
|