import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import test from "node:test"; const read = path => readFileSync(new URL(path, import.meta.url), "utf8"); const rules = read("cfg/rules.kdl"); const binds = read("cfg/keybinds.kdl"); // Source contracts only. niri-test also parses both complete theme variants; // actual screencast, client opening, inhibitor and session behavior need interaction checks. test("notification protection matches the shell's actual namespace without hiding unrelated layers", () => { const shell = read("../quickshell/nuguland/shell.qml"); const toast = shell.match(/objectName: "notificationToastSurface"[\s\S]*?WlrLayershell\.namespace: "([^"]+)"/)?.[1]; assert.ok(toast, "toast producer must declare its namespace"); const protection = rules.match(/layer-rule \{\s*match namespace="([^"]+)"\s*block-out-from "screencast"\s*\}/); assert.ok(protection, "screencast-only protection must remain"); const namespace = new RegExp(protection[1]); for (const name of ["notifications", toast]) assert.match(name, namespace); for (const name of ["nuguland-bar", "quickshell", "not-notifications", `${toast}-other`]) assert.doesNotMatch(name, namespace); }); test("non-maximized openings opt out of both maximization modes", () => { const blocks = [...rules.matchAll(/^window-rule \{\n([\s\S]*?)^\}/gm)].map(match => match[1]); const ordinary = blocks.filter(block => /^ open-maximized false$/m.test(block)); assert.equal(ordinary.length, 5); for (const block of ordinary) assert.match(block, /^ open-maximized-to-edges false$/m); const browser = blocks.find(block => block.includes('match app-id="^firefox$"')); assert.match(browser, /^ open-maximized true$/m); assert.doesNotMatch(browser, /open-maximized-to-edges/); }); test("one Steam matcher covers client and game routing", () => { const routes = [...rules.matchAll(/^window-rule \{\s*match app-id="([^"]*steam[^"]*)"\s*open-on-workspace "games"\s*\}/gm)]; assert.equal(routes.length, 1); for (const id of ["steam", "steam_app_123", "steam_app_default"]) assert.match(id, new RegExp(routes[0][1])); assert.match(rules, /match app-id="steam" title="Friends List"\s*open-floating true/); assert.ok(rules.includes('default-floating-position x=10 y=10 relative-to="bottom-right"')); }); test("inhibitor escape leaves toast dismissal and column rearrangement intact", () => { assert.match(binds, /Mod\+Shift\+Escape repeat=false allow-inhibiting=false[^\n]*\{\s*toggle-keyboard-shortcuts-inhibit\s*\}/); assert.match(binds, /Mod\+Escape [^\n]*allow-inhibiting=false \{[^}]*"notificationsDismissToasts"/); assert.ok(binds.includes('spawn "uwsm" "app" "-s" "session-graphical.slice" "--" "sh" "-c" "niri msg action focus-column-right; niri msg action move-column-to-last; niri msg action focus-window-previous"')); }); test("session manager owns shell startup and shared environment", () => { assert.doesNotMatch(read("cfg/autostart.kdl"), /^\s*spawn(?:-sh)?-at-startup\b/m); assert.doesNotMatch(read("cfg/misc.kdl"), /^\s*environment\s*\{/m); const service = read("../systemd/user/quickshell.service"); for (const property of ["PartOf=graphical-session.target", "After=graphical-session.target", "WantedBy=graphical-session.target"]) assert.ok(service.includes(property)); for (const host of ["desktop", "x230"]) { const manifest = read(`../${host}.symlinks`); assert.ok(manifest.includes("systemd/user/quickshell.service\n~/.config/systemd/user/graphical-session.target.wants/quickshell.service")); assert.ok(manifest.includes("environment.d/50-niri-wayland.conf\n~/.config/environment.d/50-niri-wayland.conf")); } });