1import * as NodeEvents from "node:events";
2import { beforeEach, expect, it, vi } from "vite-plus/test";
3import { settingsHelperBounds, watchMacSettingsWindow } from "./MacSettingsWindow.ts";
4
5const mocks = vi.hoisted(() => ({ spawn: vi.fn() }));
6vi.mock("node:child_process", () => ({ spawn: mocks.spawn }));
7let child: NodeEvents.EventEmitter & {
8 stdout: NodeEvents.EventEmitter & { setEncoding: ReturnType<typeof vi.fn> };
9 kill: ReturnType<typeof vi.fn>;
10};
11beforeEach(() => {
12 child = Object.assign(new NodeEvents.EventEmitter(), {
13 stdout: Object.assign(new NodeEvents.EventEmitter(), { setEncoding: vi.fn() }),
14 kill: vi.fn(),
15 });
16 mocks.spawn.mockReturnValue(child);
17});
18it("places the helper inside small and large Settings windows across displays", () => {
19 for (const x of [-900, 20]) {
20 for (const width of [668, 1000]) {
21 const state = { x, y: 30, width, height: 700, frontmost: true };
22 const helper = settingsHelperBounds(state);
23 expect(helper.x).toBeGreaterThanOrEqual(x + 216);
24 expect(helper.x + helper.width).toBeLessThanOrEqual(x + width - 16);
25 expect(helper.y + helper.height).toBe(714);
26 }
27 }
28});
29it("decodes partial updates and stops the one watcher process on disposal", () => {
30 const changed = vi.fn();
31 const unavailable = vi.fn();
32 const stop = watchMacSettingsWindow(changed, unavailable);
33 const state = { x: 10, y: 20, width: 723, height: 719, frontmost: true };
34 const line = JSON.stringify(state);
35 child.stdout.emit("data", line.slice(0, 8));
36 expect(changed).not.toHaveBeenCalled();
37 child.stdout.emit("data", line.slice(8) + "\nnull\n");
38 expect(changed.mock.calls).toEqual([[state], [null]]);
39 stop();
40 expect(child.kill).toHaveBeenCalledOnce();
41 child.emit("exit", 0);
42 expect(changed).toHaveBeenCalledTimes(2);
43});
44it("distinguishes unavailable tracking from a valid absent window", () => {
45 const changed = vi.fn();
46 const unavailable = vi.fn();
47 const stop = watchMacSettingsWindow(changed, unavailable);
48 child.stdout.emit("data", '{"x":"bad"}\n');
49 expect(changed).not.toHaveBeenCalled();
50 expect(unavailable).toHaveBeenCalledOnce();
51 child.emit("error", new Error("spawn failed"));
52 expect(changed).not.toHaveBeenCalled();
53 expect(unavailable).toHaveBeenCalledTimes(2);
54 stop();
55});