| 1 | // @effect-diagnostics nodeBuiltinImport:off -- Exercise the bundled KWin script in an isolated JS context. |
| 2 | import * as NodeFSP from "node:fs/promises"; |
| 3 | import * as NodeVM from "node:vm"; |
| 4 | import { expect, it } from "vite-plus/test"; |
| 5 | |
| 6 | const source = await NodeFSP.readFile(new URL("./activate.js", import.meta.url), "utf8"); |
| 7 | function signal() { |
| 8 | const callbacks: Array<(...args: unknown[]) => void> = []; |
| 9 | return { |
| 10 | connect: (callback: (...args: unknown[]) => void) => callbacks.push(callback), |
| 11 | emit: (...args: unknown[]) => callbacks.forEach((callback) => callback(...args)), |
| 12 | }; |
| 13 | } |
| 14 | function fixture() { |
| 15 | const windows: Array<{ |
| 16 | pid: number; |
| 17 | caption: string; |
| 18 | minimized: boolean; |
| 19 | captionChanged: ReturnType<typeof signal>; |
| 20 | }> = []; |
| 21 | const replies: unknown[] = []; |
| 22 | const workspace = { |
| 23 | windowList: () => windows, |
| 24 | activeWindow: undefined as (typeof windows)[number] | undefined, |
| 25 | windowAdded: signal(), |
| 26 | windowActivated: signal(), |
| 27 | }; |
| 28 | const timeout = signal(); |
| 29 | let stopped = false; |
| 30 | class Timer { |
| 31 | timeout = timeout; |
| 32 | start() {} |
| 33 | stop() { |
| 34 | stopped = true; |
| 35 | } |
| 36 | } |
| 37 | return { |
| 38 | windows, |
| 39 | workspace, |
| 40 | replies, |
| 41 | timeout, |
| 42 | stopped: () => stopped, |
| 43 | run: () => |
| 44 | NodeVM.runInNewContext(source, { |
| 45 | workspace, |
| 46 | QTimer: Timer, |
| 47 | targetPid: 123, |
| 48 | targetTitle: "Draft", |
| 49 | reply: (value: unknown) => replies.push(value), |
| 50 | }), |
| 51 | }; |
| 52 | } |
| 53 | |
| 54 | it("waits for a remapped T3 window and its title before activating", () => { |
| 55 | const f = fixture(); |
| 56 | f.run(); |
| 57 | const other = { pid: 999, caption: "Draft", minimized: false, captionChanged: signal() }; |
| 58 | f.windows.push(other); |
| 59 | f.workspace.windowAdded.emit(other); |
| 60 | expect(f.replies).toEqual([]); |
| 61 | const target = { pid: 123, caption: "", minimized: true, captionChanged: signal() }; |
| 62 | f.windows.push(target); |
| 63 | f.workspace.windowAdded.emit(target); |
| 64 | expect(f.replies).toEqual([]); |
| 65 | target.caption = "Draft"; |
| 66 | target.captionChanged.emit(); |
| 67 | expect(f.workspace.activeWindow).toBe(target); |
| 68 | expect(target.minimized).toBe(false); |
| 69 | expect(f.replies).toEqual([{ activated: true }]); |
| 70 | expect(f.stopped()).toBe(true); |
| 71 | }); |
| 72 | |
| 73 | it("rejects ambiguous destinations and ends a wait without activating a lookalike", () => { |
| 74 | const f = fixture(); |
| 75 | f.windows.push( |
| 76 | ...[1, 2].map(() => ({ |
| 77 | pid: 123, |
| 78 | caption: "Draft", |
| 79 | minimized: false, |
| 80 | captionChanged: signal(), |
| 81 | })), |
| 82 | ); |
| 83 | f.run(); |
| 84 | expect(f.replies).toEqual([{ activated: false }]); |
| 85 | expect(f.workspace.activeWindow).toBeUndefined(); |
| 86 | const waiting = fixture(); |
| 87 | waiting.run(); |
| 88 | waiting.timeout.emit(); |
| 89 | expect(waiting.replies).toEqual([{ activated: false }]); |
| 90 | expect(waiting.stopped()).toBe(true); |
| 91 | }); |