1import { assert, describe, it } from "@effect/vitest";
2import { vi } from "vite-plus/test";
3
4import {
5 activateWindowsForegroundWithApi,
6 isWindowsShellHostedForegroundWithApi,
7 type WindowsForegroundApi,
8} from "./WindowsForeground.ts";
9
10function nativeHandle(value: bigint, bytes = 8): Buffer {
11 const handle = Buffer.alloc(bytes);
12 if (bytes === 8) handle.writeBigUInt64LE(value);
13 else handle.writeUInt32LE(Number(value));
14 return handle;
15}
16
17function makeApi(input: {
18 readonly foregroundWindow?: bigint;
19 readonly currentThreadId?: number;
20 readonly foregroundThreadId?: number;
21 readonly attached?: boolean;
22 readonly activated?: boolean;
23}) {
24 return {
25 getCurrentThreadId: vi.fn(() => input.currentThreadId ?? 10),
26 getForegroundWindow: vi.fn(() => input.foregroundWindow ?? 99n),
27 getWindowClassName: vi.fn(() => "Chrome_WidgetWin_1"),
28 getWindowThreadId: vi.fn(() => input.foregroundThreadId ?? 20),
29 getWindowThreadAndProcessId: vi.fn(() => ({
30 threadId: input.foregroundThreadId ?? 20,
31 processId: 500,
32 })),
33 getWindowText: vi.fn(() => ""),
34 getWindowRect: vi.fn(() => undefined),
35 getProcessImagePath: vi.fn(() => ""),
36 isKeyDown: vi.fn(() => false),
37 attachThreadInput: vi.fn((_source, _target, attach) =>
38 attach ? (input.attached ?? true) : true,
39 ),
40 setForegroundWindow: vi.fn(() => input.activated ?? true),
41 } satisfies WindowsForegroundApi;
42}
43
44describe("Windows foreground activation", () => {
45 it.each([4, 8])("activates a %i-byte native window handle", (bytes) => {
46 const api = makeApi({});
47 const handle = nativeHandle(41n, bytes);
48
49 assert.isTrue(activateWindowsForegroundWithApi(handle, api));
50
51 assert.deepEqual(api.attachThreadInput.mock.calls, [
52 [10, 20, true],
53 [10, 20, false],
54 ]);
55 assert.deepEqual(api.setForegroundWindow.mock.calls, [[41n]]);
56 });
57
58 it("does not disturb input queues when T3 is already foreground", () => {
59 const api = makeApi({ foregroundWindow: 41n });
60
61 assert.isTrue(activateWindowsForegroundWithApi(nativeHandle(41n), api));
62
63 assert.lengthOf(api.getCurrentThreadId.mock.calls, 0);
64 assert.lengthOf(api.attachThreadInput.mock.calls, 0);
65 assert.lengthOf(api.setForegroundWindow.mock.calls, 0);
66 });
67
68 it("does not attach a thread to itself", () => {
69 const api = makeApi({ currentThreadId: 20, foregroundThreadId: 20 });
70
71 assert.isTrue(activateWindowsForegroundWithApi(nativeHandle(41n), api));
72
73 assert.lengthOf(api.attachThreadInput.mock.calls, 0);
74 assert.deepEqual(api.setForegroundWindow.mock.calls, [[41n]]);
75 });
76
77 it("uses SetForegroundWindow's result as the activation receipt", () => {
78 const api = makeApi({ activated: false });
79
80 assert.isFalse(activateWindowsForegroundWithApi(nativeHandle(41n), api));
81
82 assert.deepEqual(api.attachThreadInput.mock.calls, [
83 [10, 20, true],
84 [10, 20, false],
85 ]);
86 });
87
88 it("still asks Windows directly when the input queues cannot be attached", () => {
89 const api = makeApi({ attached: false });
90
91 assert.isTrue(activateWindowsForegroundWithApi(nativeHandle(41n), api));
92
93 assert.deepEqual(api.attachThreadInput.mock.calls, [[10, 20, true]]);
94 assert.deepEqual(api.setForegroundWindow.mock.calls, [[41n]]);
95 });
96
97 it("rejects malformed native handles", () => {
98 const api = makeApi({});
99
100 assert.throws(() => activateWindowsForegroundWithApi(Buffer.alloc(6), api));
101 assert.lengthOf(api.getForegroundWindow.mock.calls, 0);
102 });
103
104 it("recognizes a shell-hosted foreground window", () => {
105 const api = makeApi({});
106 api.getWindowClassName.mockReturnValue("ApplicationFrameWindow");
107
108 assert.isTrue(isWindowsShellHostedForegroundWithApi(api));
109 assert.deepEqual(api.getWindowClassName.mock.calls, [[99n]]);
110 });
111
112 it("does not classify ordinary or missing foreground windows as shell-hosted", () => {
113 const ordinary = makeApi({});
114 const missing = makeApi({ foregroundWindow: 0n });
115
116 assert.isFalse(isWindowsShellHostedForegroundWithApi(ordinary));
117 assert.isFalse(isWindowsShellHostedForegroundWithApi(missing));
118 assert.lengthOf(missing.getWindowClassName.mock.calls, 0);
119 });
120});