1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 normalizeLinuxPasswordStorePreference,
5 resolveLinuxPasswordStoreSwitch,
6} from "./linuxSecretStorage.ts";
7
8const autoSwitch = (env: NodeJS.ProcessEnv) =>
9 resolveLinuxPasswordStoreSwitch({ preference: "auto", env });
10
11describe("linuxSecretStorage", () => {
12 it("preserves explicit supported password-store preferences", () => {
13 expect(normalizeLinuxPasswordStorePreference("gnome-libsecret")).toBe("gnome-libsecret");
14 expect(normalizeLinuxPasswordStorePreference("kwallet")).toBe("kwallet");
15 expect(normalizeLinuxPasswordStorePreference("kwallet5")).toBe("kwallet5");
16 expect(normalizeLinuxPasswordStorePreference("kwallet6")).toBe("kwallet6");
17 });
18
19 it("falls back to auto for missing or unsupported preferences", () => {
20 expect(normalizeLinuxPasswordStorePreference(undefined)).toBe("auto");
21 expect(normalizeLinuxPasswordStorePreference("basic")).toBe("auto");
22 });
23
24 it("uses explicit preferences instead of the auto heuristic", () => {
25 for (const preference of ["gnome-libsecret", "kwallet", "kwallet5", "kwallet6"] as const) {
26 expect(
27 resolveLinuxPasswordStoreSwitch({ preference, env: { XDG_CURRENT_DESKTOP: "niri" } }),
28 ).toBe(preference);
29 // An explicit preference also wins where auto would have stayed out of the way.
30 expect(
31 resolveLinuxPasswordStoreSwitch({ preference, env: { XDG_CURRENT_DESKTOP: "KDE" } }),
32 ).toBe(preference);
33 }
34 });
35
36 it("leaves canonical KDE sessions to Electron's own wallet selection", () => {
37 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "KDE" })).toBeNull();
38 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "KDE", KDE_SESSION_VERSION: "6" })).toBeNull();
39 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "KDE", KDE_SESSION_VERSION: "5" })).toBeNull();
40 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "KDE:plasma" })).toBeNull();
41 });
42
43 it("does not force a password-store for desktops Electron already recognizes", () => {
44 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "GNOME" })).toBeNull();
45 for (const desktop of ["Deepin", "Pantheon", "UKUI", "Unity", "X-Cinnamon", "XFCE"]) {
46 expect(autoSwitch({ XDG_CURRENT_DESKTOP: desktop })).toBeNull();
47 }
48 });
49
50 it("recognizes a known desktop later in a colon-separated XDG_CURRENT_DESKTOP list", () => {
51 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "niri:GNOME" })).toBeNull();
52 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "ubuntu:GNOME" })).toBeNull();
53 });
54
55 it("forces gnome-libsecret for unrecognized Linux desktop sessions", () => {
56 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "niri" })).toBe("gnome-libsecret");
57 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "Hyprland" })).toBe("gnome-libsecret");
58 expect(autoSwitch({})).toBe("gnome-libsecret");
59 });
60
61 it("forces gnome-libsecret for desktops Electron recognizes but leaves on basic text", () => {
62 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "LXQt" })).toBe("gnome-libsecret");
63 // Chromium stops at the first recognized value, so a later name cannot rescue the session.
64 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "LXQt:GNOME" })).toBe("gnome-libsecret");
65 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "LXQt:KDE" })).toBe("gnome-libsecret");
66 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "LXQt:plasma" })).toBe("gnome-libsecret");
67 });
68
69 it("does not treat lowercase desktop names as ones Electron recognizes", () => {
70 // Chromium matches these case-sensitively, so lowercase spellings reach basic text.
71 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "gnome" })).toBe("gnome-libsecret");
72 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "xfce" })).toBe("gnome-libsecret");
73 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "kde", KDE_SESSION_VERSION: "6" })).toBe(
74 "gnome-libsecret",
75 );
76 });
77
78 it("overrides KDE sessions identified only by legacy variables", () => {
79 // Chromium reaches KWallet4 for some of these, such as DESKTOP_SESSION=kde with a version, and
80 // basic text for the rest. Either way these are the variables a previous session leaves behind,
81 // so they are treated as unproven and forced to a real keyring rather than a guessed wallet.
82 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "plasma" })).toBe("gnome-libsecret");
83 for (const session of [
84 "kde",
85 "kde-plasma",
86 "kde4",
87 "plasma",
88 "plasmawayland",
89 "plasmawayland-dev",
90 "plasmax11",
91 "plasmax11-dev",
92 ]) {
93 expect(autoSwitch({ DESKTOP_SESSION: session })).toBe("gnome-libsecret");
94 expect(autoSwitch({ XDG_SESSION_DESKTOP: session })).toBe("gnome-libsecret");
95 expect(autoSwitch({ GDMSESSION: session })).toBe("gnome-libsecret");
96 }
97 expect(autoSwitch({ DESKTOP_SESSION: "kde", KDE_SESSION_VERSION: "6" })).toBe(
98 "gnome-libsecret",
99 );
100 expect(autoSwitch({ KDE_SESSION_VERSION: "6" })).toBe("gnome-libsecret");
101 expect(autoSwitch({ KDE_FULL_SESSION: "true", KDE_SESSION_VERSION: "6" })).toBe(
102 "gnome-libsecret",
103 );
104 });
105
106 it("ignores stale session hints when XDG_CURRENT_DESKTOP is authoritative", () => {
107 expect(
108 autoSwitch({ XDG_CURRENT_DESKTOP: "niri", DESKTOP_SESSION: "gnome", GDMSESSION: "gnome" }),
109 ).toBe("gnome-libsecret");
110 // A previous KDE session left these behind; the compositor running now is not KDE.
111 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "niri", KDE_SESSION_VERSION: "6" })).toBe(
112 "gnome-libsecret",
113 );
114 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "niri", XDG_SESSION_DESKTOP: "GNOME" })).toBe(
115 "gnome-libsecret",
116 );
117 expect(autoSwitch({ XDG_CURRENT_DESKTOP: "niri:plasma" })).toBe("gnome-libsecret");
118 expect(
119 autoSwitch({
120 XDG_CURRENT_DESKTOP: "Hyprland",
121 XDG_SESSION_DESKTOP: "KDE",
122 KDE_SESSION_VERSION: "6",
123 }),
124 ).toBe("gnome-libsecret");
125 });
126});