Open History to see the last commit
1import { expect, it } from "vite-plus/test";
2import { CaptureService, CLIENT_NAMES, isWaylandSession } from "./captureService.js";
3
4it("supports GNOME 50 without the removed X11 API, rejecting older X11 sessions", () => {
5 expect(isWaylandSession({})).toBe(true);
6 expect(isWaylandSession({ is_wayland_compositor: () => true })).toBe(true);
7 expect(isWaylandSession({ is_wayland_compositor: () => false })).toBe(false);
8});
9
10function fixture(overrides = {}) {
11 let snapshots = 0;
12 const options = {
13 getNameOwner: async (name) => (name === CLIENT_NAMES[0] ? ":1.23" : null),
14 isAvailable: () => true,
15 takeSnapshot: async () => {
16 snapshots++;
17 return "pixels";
18 },
19 ...overrides,
20 };
21 return { service: new CaptureService(options), snapshots: () => snapshots };
22}
23
24it("rejects other clients before looking at the active window", async () => {
25 const { service, snapshots } = fixture();
26 await expect(service.capture(":1.99")).rejects.toThrow("Only T3 Code");
27 expect(snapshots()).toBe(0);
28 expect(await service.capture(":1.23")).toBe("pixels");
29});
30
31it("rechecks the name owner on each capture", async () => {
32 let owner = ":1.23";
33 const { service } = fixture({ getNameOwner: async () => owner });
34 expect(await service.capture(owner)).toBe("pixels");
35 owner = ":1.24";
36 await expect(service.capture(":1.23")).rejects.toThrow("Only T3 Code");
37});
38
39it("rejects locked and non-Wayland sessions without taking a screenshot", async () => {
40 const { service, snapshots } = fixture({ isAvailable: () => false });
41 await expect(service.capture(":1.23")).rejects.toThrow("unavailable");
42 expect(snapshots()).toBe(0);
43});
44
45it("does not start a screenshot if disabled during authorization", async () => {
46 let authorize;
47 const { service, snapshots } = fixture({
48 getNameOwner: () =>
49 new Promise((resolve) => {
50 authorize = resolve;
51 }),
52 });
53 const result = service.capture(":1.23");
54 service.disable();
55 authorize(":1.23");
56 await expect(result).rejects.toThrow("unavailable");
57 expect(snapshots()).toBe(0);
58});
59
60it.each(["lock", "disable"])(
61 "does not return pixels after %s during capture",
62 async (operation) => {
63 let available = true;
64 const { service } = fixture({
65 isAvailable: () => available,
66 takeSnapshot: async () => {
67 if (operation === "disable") service.disable();
68 else available = false;
69 return "private pixels";
70 },
71 });
72 await expect(service.capture(":1.23")).rejects.toThrow("unavailable");
73 },
74);
75
76it("rejects concurrent requests and clears busy after a failed capture", async () => {
77 let finish;
78 const started = Promise.withResolvers();
79 const { service } = fixture({
80 takeSnapshot: () => {
81 started.resolve();
82 return new Promise((_, reject) => {
83 finish = reject;
84 });
85 },
86 });
87 const first = service.capture(":1.23");
88 await started.promise;
89 await expect(service.capture(":1.23")).rejects.toThrow("already in progress");
90 finish(new Error("gone"));
91 await expect(first).rejects.toThrow("gone");
92 await expect(service.capture(":1.99")).rejects.toThrow("Only T3 Code");
93});
94
95it("prepares focus/effects only after pixels and identity have been captured", async () => {
96 const events = [];
97 const snapshot = { png: "pixels", metadata: "captured-window" };
98 const { service } = fixture({
99 getProcessId: async () => {
100 events.push("caller-pid");
101 return 42;
102 },
103 takeSnapshot: async () => {
104 events.push("snapshot");
105 return snapshot;
106 },
107 beginFeedback: (sender, pid, captured, options) => {
108 expect([sender, pid, captured, options]).toEqual([
109 ":1.23",
110 42,
111 snapshot,
112 { flash: true, animate: true },
113 ]);
114 events.push("feedback");
115 return true;
116 },
117 });
118 expect(await service.capture(":1.23", { flash: true, animate: true })).toEqual({
119 ...snapshot,
120 animationStarted: true,
121 });
122 expect(events).toEqual(["caller-pid", "snapshot", "feedback"]);
123});
124
125it("does not create overlays or activate after the session locks during capture", async () => {
126 let available = true;
127 let feedback = 0;
128 const { service } = fixture({
129 getProcessId: async () => 42,
130 isAvailable: () => available,
131 takeSnapshot: async () => {
132 available = false;
133 return {};
134 },
135 beginFeedback: () => {
136 feedback++;
137 },
138 });
139 await expect(service.capture(":1.23", { flash: true, animate: true })).rejects.toThrow(
140 "unavailable",
141 );
142 expect(feedback).toBe(0);
143});