repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/niri.test.mjs
Rawimport assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import vm from "node:vm";
const read = name => readFileSync(new URL(name, import.meta.url), "utf8");
const protocol = vm.createContext({});
vm.runInContext(read("niri.js"), protocol);
const qml = read("Niri.qml");
const plain = value => JSON.parse(JSON.stringify(value));
const equal = (got, want, message) => assert.deepEqual(plain(got), plain(want), message);
const layout = (width = 800) => ({
pos_in_scrolling_layout: [1, 1], tile_size: [width, 600], window_size: [width, 600],
tile_pos_in_workspace_view: [0, 0], window_offset_in_tile: [0, 0]
});
const win = (id, focused = false) => ({
id, title: `window ${id}`, app_id: "synthetic.app", pid: 123, workspace_id: 1,
is_focused: focused, is_floating: false, is_urgent: false, layout: layout(), focus_timestamp: null
});
const ws = (id, output, active = false, focused = false) => ({
id, idx: id + 1, name: null, output, is_urgent: false, is_active: active,
is_focused: focused, active_window_id: null
});
const cast = (target = { Nothing: {} }, kind = "PipeWire") => ({
stream_id: 1, session_id: 1, kind, target, is_dynamic_target: false,
is_active: false, pid: null, pw_node_id: null
});
const output = (name, x) => ({
name, make: "fixture", model: "panel", serial: null, physical_size: [300, 200],
modes: [{ width: 800, height: 600, refresh_rate: 60000, is_preferred: true }],
current_mode: 0, is_custom_mode: false, vrr_supported: false, vrr_enabled: false,
logical: { x, y: 0, width: 800, height: 600, scale: 1, transform: "Normal" }
});
// Replay the actual QML function bodies, with only process/timer boundaries fake.
// This is not a QML load, timer, input, or visual test.
function service() {
const process = () => ({
running: false, commands: [], signals: [],
exec(command) { this.commands.push(plain(command)); this.running = true; },
signal(number) { this.signals.push(number); }
});
const state = vm.createContext({
NiriState: protocol, available: true, ready: false, windowsInitialized: false,
workspacesInitialized: false, streamFailed: false, retryDelay: 250,
lastError: "", outputError: "", outputsPending: false, outputsFinished: false,
windows: [], workspaces: [], outputs: {}, focusedWindow: null, overviewState: "unknown",
lastWindowAction: {}, workspaceShortcuts: {}, actionQueue: [],
query: process(), outputQuery: process(), actionProc: process(),
reconnect: {
running: false, interval: 0, starts: 0,
stop() { this.running = false; },
start() { this.running = true; this.starts++; }
},
captured: [], screenshotCaptured(path) { state.captured.push(path); }
});
state.root = state;
for (const match of qml.matchAll(/^ function (\w+)\(([^)]*)\) \{([\s\S]*?)^ \}/gm))
vm.runInContext(`function ${match[1]}(${match[2]}) {${match[3]}\n}`, state);
Object.defineProperty(state, "focusedWindowRecord", { get: () => state.windowById(state.focusedWindow) });
return state;
}
const send = (state, name, data) => state.applyEvent(JSON.stringify({ [name]: data }));
function initialize(state, windows = [win(0, true), win(2)], workspaces = [ws(1, "DP-1", true, true), ws(2, "DP-1"), ws(3, "DP-2", true)]) {
send(state, "WorkspacesChanged", { workspaces });
send(state, "WindowsChanged", { windows });
send(state, "OverviewOpenedOrClosed", { is_open: false });
assert.equal(state.ready, true);
}
const state = service();
state.refresh();
equal(state.query.commands, [["niri", "msg", "--json", "event-stream"]]);
state.refresh();
assert.equal(state.query.commands.length, 1, "never overlap subscriptions");
send(state, "FutureEvent", null);
assert.equal(state.ready, false, "unknown events cannot establish readiness");
send(state, "ScreenshotCaptured", { path: null });
assert.equal(state.ready, false, "capture is not initial state");
send(state, "WorkspacesChanged", { workspaces: [ws(1, "DP-1", true, true), ws(2, "DP-1"), ws(3, "DP-2", true)] });
assert.equal(state.ready, false);
send(state, "WindowsChanged", { windows: [win(0, true), win(2)] });
assert.equal(state.focusedWindow, 0, "initial focus allows id zero");
assert.equal(state.ready, false, "overview initial state is also required");
send(state, "OverviewOpenedOrClosed", { is_open: false });
assert.equal(state.ready, true);
assert.equal(state.focusedWindowRecord.app_id, "synthetic.app");
send(state, "WorkspaceActivated", { id: 2, focused: false });
equal(state.workspaces.map(row => [row.is_active, row.is_focused]), [[false, true], [true, false], [true, false]]);
send(state, "WorkspaceActivated", { id: 3, focused: true });
equal(state.workspaces.map(row => [row.is_active, row.is_focused]), [[false, false], [true, false], [true, true]]);
assert.equal(state.focusedOutputNameFromWorkspaces(state.workspaces), "DP-2");
// Cross-part ordering is explicitly non-atomic upstream.
send(state, "WorkspaceActiveWindowChanged", { workspace_id: 1, active_window_id: 99 });
assert.equal(state.workspaces[0].active_window_id, 99);
assert.equal(state.windowById(99), null);
assert(state.displayWorkspaces(state.workspaces).some(row => row.id === 1));
send(state, "WorkspaceActiveWindowChanged", { workspace_id: 1, active_window_id: null });
assert(!state.displayWorkspaces(state.workspaces).some(row => row.id === 1));
for (const urgent of [true, false]) {
send(state, "WorkspaceUrgencyChanged", { id: 1, urgent });
assert.equal(state.workspaces[0].is_urgent, urgent);
assert.equal(state.displayWorkspaces(state.workspaces).some(row => row.id === 1), urgent);
}
send(state, "WindowOpenedOrChanged", { window: win(99, true) });
equal(state.windows.filter(row => row.is_focused).map(row => row.id), [99]);
assert.equal(state.focusedWindow, 99);
const changed = { ...win(99, true), title: "renamed", app_id: "synthetic.vault-context", workspace_id: null };
send(state, "WindowOpenedOrChanged", { window: changed });
assert.equal(state.windows.length, 3);
equal(state.focusedWindowRecord, changed, "whole record replacement, including title/app-id");
send(state, "WindowOpenedOrChanged", { window: { ...changed, is_focused: false, title: null, app_id: null } });
assert.equal(state.focusedWindow, null, "unfocused replacement of focused window clears derived focus");
send(state, "WindowFocusChanged", { id: 0 });
equal(state.windows.filter(row => row.is_focused).map(row => row.id), [0]);
assert.equal(state.focusedWindow, 0);
send(state, "WindowFocusChanged", { id: null });
assert.equal(state.focusedWindow, null);
assert(state.windows.every(row => !row.is_focused));
send(state, "WindowFocusChanged", { id: 999 });
assert.equal(state.focusedWindow, null, "reference reducer only focuses records it has");
send(state, "WindowFocusChanged", { id: 99 });
for (const urgent of [true, false]) {
send(state, "WindowUrgencyChanged", { id: 99, urgent });
assert.equal(state.windowById(99).is_urgent, urgent);
}
for (const focus_timestamp of [{ secs: 12, nanos: 34 }, null]) {
send(state, "WindowFocusTimestampChanged", { id: 99, focus_timestamp });
equal(state.windowById(99).focus_timestamp, focus_timestamp);
}
send(state, "WindowLayoutsChanged", { changes: [[0, layout(900)], [99, layout(1200)]] });
equal(state.windowById(0).layout, layout(900));
equal(state.windowById(99).layout, layout(1200));
send(state, "WindowClosed", { id: 2 });
assert.equal(state.focusedWindow, 99, "closing a different window preserves focus");
send(state, "WindowClosed", { id: 99 });
assert.equal(state.focusedWindow, null);
assert.equal(state.windowById(99), null);
send(state, "WindowsChanged", { windows: [win(9, true)] });
equal(state.windows.map(row => row.id), [9], "full stream state replaces old records");
send(state, "WorkspacesChanged", { workspaces: [ws(0, null)] });
equal(state.workspaces.map(row => row.id), [0]);
send(state, "WorkspaceActivated", { id: 0, focused: true });
assert.equal(state.workspaces[0].is_focused, true);
send(state, "OverviewOpenedOrClosed", { is_open: true });
assert.equal(state.overviewState, "open");
for (const path of ["/tmp/synthetic.png", null, ""]) send(state, "ScreenshotCaptured", { path });
equal(state.captured, [null, "/tmp/synthetic.png", null, ""], "emit upstream paths verbatim, including null");
// Outputs cannot overwrite streamed focus, windows, workspaces, or overview,
// even when a response arrives after newer deltas (the old snapshot race).
const streamed = plain([state.windows, state.workspaces, state.focusedWindow, state.overviewState]);
const inventory = { "DP-1": output("DP-1", 800), "DP-2": output("DP-2", -800) };
state.applyOutputs(JSON.stringify(inventory), 0, 0);
equal([state.windows, state.workspaces, state.focusedWindow, state.overviewState], streamed);
equal(state.outputs, inventory);
equal(state.outputList().map(row => row.name), ["DP-1", "DP-2"]);
send(state, "WorkspacesChanged", { workspaces: [ws(1, "DP-1", true), ws(2, "DP-2", true)] });
equal(state.workspaceGroups().map(row => row.output), ["DP-2", "DP-1"]);
state.applyOutputs(JSON.stringify({ ...inventory, "DP-1": output("DP-1", -1600) }), 0, 0);
equal(state.workspaceGroups().map(row => row.output), ["DP-1", "DP-2"], "geometry-only inventory refresh reorders groups");
const lastInventory = plain(state.outputs);
for (const [text, code, status] of [
["{}", 1, 0], ["{}", 0, 1], ["", 0, 0], ["{", 0, 0], ["null", 0, 0], ["[]", 0, 0],
['{"DP-1":{}}', 0, 0], [JSON.stringify({ "DP-1": { ...output("DP-1", 0), logical: {} } }), 0, 0]
]) {
state.applyOutputs(text, code, status);
assert(state.outputError.length > 0);
equal(state.outputs, lastInventory, "failed inventory remains intact");
assert.equal(state.ready, true, "output failure does not invalidate stream readiness");
}
state.applyOutputs(JSON.stringify({ "DP-1": { ...output("DP-1", 0), logical: null, current_mode: null } }), 0, 0);
assert.equal(state.outputX("DP-1"), 1000000, "disabled output has no physical order");
assert.equal(state.outputError, "");
state.applyOutputs("{}", 0, 0);
equal(state.outputList(), [], "successful empty inventory removes disconnected outputs");
state.refreshOutputs(); state.refreshOutputs(); state.refreshOutputs();
assert.equal(state.outputQuery.commands.length, 1);
assert.equal(state.outputsPending, true);
state.outputQuery.running = false;
state.refreshOutputs();
assert.equal(state.outputsPending, false);
equal(state.outputQuery.commands[1], ["niri", "msg", "--json", "outputs"]);
// Validation is atomic, never an empty-success fallback or a swallowed error.
const malformed = {
WorkspacesChanged: [{}, { workspaces: null }, { workspaces: [ws(1, "DP-1"), ws(1, "DP-1")] }, { workspaces: [{ ...ws(1, "DP-1"), is_active: 1 }] }],
WorkspaceActivated: [{ id: 1, focused: "true" }, { id: 999, focused: true }],
WorkspaceActiveWindowChanged: [{ workspace_id: 1 }, { workspace_id: 999, active_window_id: null }],
WorkspaceUrgencyChanged: [{ id: 1, urgent: 1 }],
WindowsChanged: [{}, { windows: [win(0), win(0)] }, { windows: [{ ...win(0), layout: {} }] }],
WindowOpenedOrChanged: [{}, { window: { ...win(0), title: 1 } }, { window: { ...win(0), is_focused: null } }],
WindowClosed: [{ id: "0" }, { id: 999 }],
WindowFocusChanged: [{}, { id: false }, { id: -1 }, { id: 0.5 }],
WindowFocusTimestampChanged: [{ id: 0 }, { id: 0, focus_timestamp: { secs: 1, nanos: 1000000000 } }],
WindowUrgencyChanged: [{ id: 0, urgent: "false" }],
WindowLayoutsChanged: [{}, { changes: [[0, layout(1000)], [999, layout()]] }, { changes: [[0, {}]] }],
OverviewOpenedOrClosed: [{}, { is_open: 1 }],
ScreenshotCaptured: [{}, { path: 123 }, { path: false }],
KeyboardLayoutsChanged: [{}, { keyboard_layouts: { names: [1], current_idx: 0 } }],
KeyboardLayoutSwitched: [{}, { idx: 256 }],
ConfigLoaded: [{}, { failed: "false" }],
CastsChanged: [{}, { casts: [null] }],
CastStartedOrChanged: [{}, { cast: { ...cast(), target: { Window: { id: "1" } } } }],
CastStopped: [{}, { stream_id: -1 }]
};
for (const [name, payloads] of Object.entries(malformed)) {
for (const payload of [null, false, ...payloads]) {
const s = service(); initialize(s);
const before = plain([s.windows, s.workspaces, s.focusedWindow, s.overviewState]);
send(s, name, payload);
assert.equal(s.ready, false, `${name} rejects malformed payload`);
assert.equal(s.streamFailed, true, name);
assert.match(s.lastError, /malformed niri/);
equal(s.query.signals, [9], "bad stream is terminated for resynchronization");
equal([s.windows, s.workspaces, s.focusedWindow, s.overviewState], before, "no partial mutation");
equal(s.captured, []);
send(s, "WindowsChanged", { windows: [] });
equal(s.windows, before[0], "ignore remaining data from invalidated stream");
}
}
for (const line of ["{", "null", "[]", "{}", '{"WindowsChanged":{},"FutureEvent":{}}']) {
const s = service(); s.applyEvent(line);
assert.equal(s.streamFailed, true, line);
assert.equal(s.ready, false);
}
const unowned = service();
for (const [name, payload] of [
["KeyboardLayoutsChanged", { keyboard_layouts: { names: ["English (US)"], current_idx: 0 } }],
["KeyboardLayoutSwitched", { idx: 0 }], ["ConfigLoaded", { failed: false }],
["CastsChanged", { casts: [cast()] }], ["CastsChanged", { casts: [] }],
["CastStartedOrChanged", { cast: cast({ Output: { name: "DP-1" } }, "WlrScreencopy") }],
["CastStartedOrChanged", { cast: { ...cast({ Window: { id: 0 } }), pid: 123, pw_node_id: 4 } }],
["CastStopped", { stream_id: 1 }]
]) {
send(unowned, name, payload);
assert.equal(unowned.ready, false, `${name} is not owned state`);
assert.equal(unowned.streamFailed, false, `${name} accepts its installed payload`);
}
const ignored = service(); initialize(ignored);
const beforeIgnored = plain([ignored.windows, ignored.workspaces]);
for (const name of ["FutureEvent", "OutputsChanged"])
send(ignored, name, { outputs: [] });
equal([ignored.windows, ignored.workspaces], beforeIgnored);
assert.equal(ignored.streamFailed, false);
send(ignored, "WindowFocusChanged", { id: 0, future_field: 42 });
assert.equal(ignored.focusedWindow, 0, "unknown fields remain tolerated");
for (const name of ["WorkspaceUrgencyChanged", "WindowUrgencyChanged"])
send(ignored, name, { id: 999, urgent: true });
send(ignored, "WindowFocusTimestampChanged", { id: 999, focus_timestamp: null });
assert.equal(ignored.streamFailed, false, "reference reducer ignores missing urgency/timestamp targets");
// Disconnect resets all streamed state, not the independently owned inventory.
const reconnecting = service(); initialize(reconnecting);
reconnecting.outputs = inventory;
reconnecting.streamDisconnected();
assert.equal(reconnecting.ready, false);
equal(reconnecting.windows, []); equal(reconnecting.workspaces, []);
assert.equal(reconnecting.focusedWindow, null);
assert.equal(reconnecting.overviewState, "unknown");
equal(reconnecting.outputs, inventory);
assert.equal(reconnecting.reconnect.interval, 250);
reconnecting.streamDisconnected();
assert.equal(reconnecting.reconnect.starts, 1, "duplicate disconnect notification is coalesced");
for (let attempt = 0; attempt < 20; attempt++) {
reconnecting.reconnect.running = false;
reconnecting.streamDisconnected();
assert(reconnecting.reconnect.interval >= 250 && reconnecting.reconnect.interval <= 8000);
}
assert.equal(reconnecting.reconnect.interval, 8000);
reconnecting.refresh();
assert.equal(reconnecting.streamFailed, false);
assert.equal(reconnecting.reconnect.running, false);
send(reconnecting, "WindowsChanged", { windows: [] });
assert.equal(reconnecting.ready, false, "old initialization flags cannot satisfy a new stream");
send(reconnecting, "WorkspacesChanged", { workspaces: [] });
assert.equal(reconnecting.ready, false);
send(reconnecting, "OverviewOpenedOrClosed", { is_open: false });
assert.equal(reconnecting.ready, true);
assert.equal(reconnecting.retryDelay, 250);
reconnecting.failStream("event stream initialization timed out");
assert.equal(reconnecting.ready, false);
equal(reconnecting.query.signals, [9]);
const unavailable = service(); unavailable.available = false;
unavailable.refresh(); unavailable.refreshOutputs(); unavailable.streamDisconnected();
equal(unavailable.query.commands, []); equal(unavailable.outputQuery.commands, []);
assert.equal(unavailable.reconnect.running, false);
// Dormant IPC contract remains metadata only. Internal row id is not a CLI verb.
const metadata = service(); initialize(metadata, [{ ...win(0, true), workspace_id: 0 }]);
const card = JSON.parse(metadata.windowCardAction("toggle-fullscreen"));
assert.equal(card.id, 0); assert.equal(card.workspace, 0);
assert.equal(card.lastWindowAction.accepted, true);
assert.equal(card.lastWindowAction.niriAction, "fullscreen-window");
assert.equal(card.lastWindowAction.executed, false);
assert(card.actions.every(row => row.label === row.label.toLowerCase()));
for (const kind of ["focus", "move-workspace", "close-window"])
assert.equal(JSON.parse(metadata.windowCardAction(kind)).lastWindowAction.accepted, true);
assert.equal(JSON.parse(metadata.windowCardAction("unknown")).lastWindowAction.accepted, false);
equal(metadata.actionProc.commands, [], "metadata never dispatches actions");
metadata.screenshot("/tmp/selection.png"); metadata.screenshotScreen("/tmp/screen.png"); metadata.screenshotWindow("/tmp/window.png");
assert.equal(metadata.actionProc.commands.length, 1, "rapid actions cannot terminate the preceding request");
assert.equal(metadata.actionQueue.length, 2);
for (let i = 0; i < 2; i++) {
metadata.actionProc.running = false;
metadata.dispatchNextAction();
}
assert.equal(metadata.actionQueue.length, 0);
equal(metadata.actionProc.commands, [
["niri", "msg", "action", "screenshot", "--path", "/tmp/selection.png"],
["niri", "msg", "action", "screenshot-screen", "--path", "/tmp/screen.png", "--write-to-disk", "true"],
["niri", "msg", "action", "screenshot-window", "--path", "/tmp/window.png", "--write-to-disk", "true"]
]);
// Wiring checks supplement, not replace, the parent's nested integration gates.
assert.doesNotMatch(qml, /loadSnapshot|applySnapshot|id: snapshot|"OutputsChanged"/);
assert.match(qml, /signal screenshotCaptured\(var path\)/);
assert.match(qml, /onRunningChanged: if \(!running\)\s*root\.streamDisconnected\(\)/, "handles clean exit, crash, and failed start");
assert.match(qml, /Connections \{\s*target: outputQuery\s*function onExited\(code, status\) \{\s*root.outputsFinished = true;\s*root.applyOutputs\(outputStdout.text, code, status\)/);
assert.match(qml, /Connections \{\s*target: query\s*function onExited\(code\)/);
assert.match(qml, /id: actionProc\s*onRunningChanged: if \(!running\)\s*root.dispatchNextAction\(\)/);
assert.match(qml, /target: Quickshell[\s\S]*function onScreensChanged\(\)/);
assert.match(qml, /interval: 5000\s*running: root.available\s*repeat: true\s*onTriggered: root.refreshOutputs\(\)/);
assert.match(qml, /interval: 3000\s*running: outputQuery.running\s*onTriggered: outputQuery.signal\(9\)/);
assert.match(qml, /running: query.running && !root.ready && !root.streamFailed/);
assert.match(qml, /onTriggered: root.refresh\(\)/);
console.log("niri.test.mjs: stream, lifecycle, outputs, screenshot signal, and metadata replays PASS (not UI validation)");