repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/test-fixtures/ui/niri-integration.mjs
Rawimport assert from 'node:assert/strict';
import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { setTimeout as delay } from 'node:timers/promises';
// Verify a kernel-owned direct child, never a process-name search or parent PID.
export function assertSubscription(pid, qmlPid, status, command) {
assert(Number.isSafeInteger(pid) && pid > 1 && Number.isSafeInteger(qmlPid) && qmlPid > 1);
assert.equal(Number(status.match(/^PPid:\s+(\d+)$/m)?.[1]), qmlPid, 'subscription must be a direct QML child');
assert.deepEqual(command.split('\0').filter(Boolean), ['/usr/bin/niri', 'msg', '--json', 'event-stream']);
}
export function pngSize(bytes) {
assert(bytes.length > 32, 'capture is not a complete PNG header');
assert.deepEqual(bytes.subarray(0, 8), Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
assert.equal(bytes.toString('ascii', 12, 16), 'IHDR');
const size = { width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20) };
assert(size.width > 0 && size.height > 0);
return size;
}
export async function prepareNiriIntegration({ run, start, waitFor, runtime, nativeNiri, nativeSocket, assertPureService }) {
writeFileSync('/project/Niri.qml', nativeNiri);
const vault = readFileSync('/project/test-fixtures/ui/niri-integration-vault.qml', 'utf8');
assertPureService(vault);
writeFileSync('/project/Vault.qml', vault);
writeFileSync('/project/niri-integration-ui-test.qml', readFileSync('/project/test-fixtures/ui/niri-integration.qml', 'utf8').replace('import "../.." as N', 'import "." as N'));
const protocol = '/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml';
run('wayland-scanner', ['client-header', protocol, '/tmp/niri-integration-xdg.h']);
run('wayland-scanner', ['private-code', protocol, '/tmp/niri-integration-xdg.c']);
run('cc', ['-Wall', '-Wextra', '-Werror', '-I/tmp', '/project/test-fixtures/ui/niri-integration-client.c', '/tmp/niri-integration-xdg.c', '-lwayland-client', '-o', '/tmp/niri-integration-client']);
const socket = join(runtime, 'niri.integration.sock');
const modeFile = join(runtime, 'niri-integration-outputs.json');
writeFileSync(modeFile, 'null');
start('node', ['/project/test-fixtures/ui/niri-integration-proxy.mjs', socket, nativeSocket, modeFile], 'niri-integration-proxy');
await waitFor('output fixture socket', () => existsSync(socket));
return { socket, modeFile };
}
export async function runNiriIntegration({ run, start, check, snapshot, ipc, screenshot, click, input, key, compositorKey, stop, evidence, runtime, modeFile }) {
const action = (...args) => run('niri', ['msg', 'action', ...args.map(String)]);
const query = name => JSON.parse(run('niri', ['msg', '--json', name]));
const file = name => join(evidence, `${name}.png`);
async function closeClient(client) {
action('close-window', '--id', client.id);
let timeout;
try {
if (client.child.exitCode === null) await new Promise((resolve, reject) => {
timeout = setTimeout(() => reject(new Error('native client did not acknowledge close')), 3000);
client.child.once('exit', resolve);
});
assert.equal(client.child.exitCode, 0, 'xdg close must exit normally');
assert.equal(client.child.signalCode, null);
await stop(client.child);
} finally {
clearTimeout(timeout);
}
}
const saved = async (kind, name) => {
const path = file(name);
const before = snapshot().captures.recentCount;
const submitted = JSON.parse(ipc('capture', kind, path));
assert.equal(submitted.lastAction.status, 'pending');
assert.equal(submitted.recentCount, before, 'dispatch must not record success');
const state = await check(`${kind} saves only on exact native completion: ${name}`, s => s.captures.recentCount === before + 1 && s.captures.recent[0].path === path && s.captures.recent[0].saved);
const bytes = readFileSync(path);
const dimensions = pngSize(bytes);
// Decode the entire image independently, not just its existence/header.
run('identify', ['-regard-warnings', path]);
appendFileSync(join(evidence, 'niri-files.jsonl'), JSON.stringify({ path, ...dimensions, size: bytes.length, id: state.captures.recent[0].id }) + '\n');
return dimensions;
};
let state = await check('real stream initial state and actual Bar pills', s => s.niri.ready && s.niri.outputCount === 1 && s.niri.windowCount === 0 && s.pills.length === 1 && s.pills[0].active);
const firstWorkspace = state.niri.workspaces.find(row => row.is_focused).idx;
async function client(name, title, app) {
const fifo = join(runtime, `niri-integration-${name}.fifo`);
run('mkfifo', [fifo]);
const child = start('/tmp/niri-integration-client', [fifo, title, app], `niri-client-${name}`);
await check(`native xdg window opens: ${name}`, s => s.niri.focusedTitle === title && s.niri.focusedAppId === app);
return { child, fifo, id: snapshot().niri.focusedWindow };
}
const alpha = await client('alpha', 'alpha initial', 'synthetic.alpha');
writeFileSync(alpha.fifo, 'title alpha renamed\n');
await check('focused title delta replaces existing record', s => s.niri.windowCount === 1 && s.niri.focusedTitle === 'alpha renamed' && s.text.includes('focus: alpha renamed · synthetic.alpha'));
writeFileSync(alpha.fifo, 'app synthetic.changed\n');
state = await check('focused app-id delta reaches visible context', s => s.niri.focusedAppId === 'synthetic.changed');
click(state.vault.rect);
await check('actual Bar forwards focused app to safe vault sink', s => s.vault.opens === 1 && s.vault.app === 'synthetic.changed' && s.vault.opened === 'vault');
const beta = await client('beta', 'beta window', 'synthetic.beta');
action('move-window-to-workspace', '2');
state = await check('workspace deltas add populated second pill', s => s.pills.length === 2 && s.niri.workspaces.some(row => row.idx === 2 && row.active_window_id === beta.id));
click(state.pills.find(row => row.index === String(firstWorkspace)).rect);
state = await check('actual Bar pill focuses first workspace and alpha', s => s.niri.focusedWindow === alpha.id && s.pills.find(row => row.index === String(firstWorkspace))?.active);
click(state.vault.rect);
await check('vault context updates on each opening after external focus', s => s.vault.opens === 2 && s.vault.app === 'synthetic.changed');
action('focus-window', '--id', beta.id);
state = await check('external focus reaches second workspace and beta', s => s.niri.focusedWindow === beta.id && s.pills.find(row => row.index === '2')?.active);
click(state.vault.rect);
await check('vault receives changed focused app, not retained prior context', s => s.vault.opens === 3 && s.vault.app === 'synthetic.beta');
await screenshot('niri-bar-two-workspaces');
action('toggle-overview');
await check('native overview opens through stream', s => s.niri.overviewState === 'open');
await compositorKey('Escape');
await check('keyboard closes native overview', s => s.niri.overviewState === 'closed');
// Only the subscription dies. Mutate a client before the bounded retry fires.
state = snapshot();
const pid = state.subscriptionPid;
assertSubscription(pid, state.qmlPid, readFileSync(`/proc/${pid}/status`, 'utf8'), readFileSync(`/proc/${pid}/cmdline`, 'utf8'));
const transitionCount = state.readiness.length;
const originalQmlPid = state.qmlPid;
writeFileSync(join(evidence, 'niri-subscription.json'), JSON.stringify({ pid, qmlPid: state.qmlPid, command: ['/usr/bin/niri', 'msg', '--json', 'event-stream'] }, null, 2));
process.kill(pid, 'SIGTERM');
writeFileSync(beta.fifo, 'title beta during disconnect\n');
state = await check('subscription disconnect clears readiness and reconnects with fresh state', s => s.readiness.slice(transitionCount).some(row => !row.ready) && s.niri.ready && s.subscriptionPid !== pid && s.niri.focusedTitle === 'beta during disconnect');
assert.equal(state.qmlPid, originalQmlPid);
assert(state.readiness.slice(transitionCount).find(row => !row.ready).windows === 0);
const original = query('outputs');
const outputName = Object.keys(original)[0];
run('niri', ['msg', 'output', outputName, 'position', 'set', '400', '120']);
const repositioned = query('outputs');
writeFileSync(join(evidence, 'niri-reposition.json'), JSON.stringify({ original, repositioned }, null, 2));
assert.equal(repositioned[outputName].logical.x, 400);
assert.equal(repositioned[outputName].logical.y, 120);
await check('real output reposition reaches outputs-only poll', s => s.niri.outputs[outputName].logical.x === 400 && s.niri.outputs[outputName].logical.y === 120);
run('niri', ['msg', 'output', outputName, 'position', 'set', '0', '0']);
const stable = snapshot().niri;
const extra = { ...original[outputName], name: 'fixture-hotplug', logical: { ...original[outputName].logical, x: -1280 } };
assert.equal(ipc('fixtureOutputs', 'true'), 'true');
writeFileSync(modeFile, JSON.stringify({ Ok: { Outputs: { ...original, 'fixture-hotplug': extra } } }));
await check('fixture hotplug enters production outputs query without resetting stream', s => s.niri.outputCount === 2 && s.niri.outputs['fixture-hotplug'].logical.x === -1280 && s.niri.focusedTitle === stable.focusedTitle && s.niri.windowCount === stable.windowCount);
writeFileSync(modeFile, JSON.stringify({ Err: 'controlled output query rejection' }));
await check('rejected outputs query preserves last inventory and stream readiness', s => s.niri.outputError.length > 0 && s.niri.outputCount === 2 && s.niri.ready);
writeFileSync(modeFile, 'null');
await check('fixture unplug restores real inventory through periodic polling', s => s.niri.outputCount === 1 && s.niri.outputError === '' && !s.niri.outputs['fixture-hotplug']);
assert.equal(ipc('fixtureOutputs', 'false'), 'true');
assert.deepEqual(await saved('focused-screen', 'native-screen'), { width: 1280, height: 720 });
assert.deepEqual(await saved('focused-window', 'native-window'), { width: 480, height: 280 });
let submitted = JSON.parse(ipc('capture', 'interactive', file('native-interactive')));
assert.equal(submitted.lastAction.saved, false);
assert.equal(submitted.recentCount, 2);
await delay(350);
input('mousemove', '100', '100');
input('mousedown', '1');
input('mousemove', '500', '300');
input('mouseup', '1');
await screenshot('niri-selection-pending');
await compositorKey('Return');
await check('keyboard confirms native interactive save', s => s.captures.recentCount === 3 && s.captures.recent[0].path === file('native-interactive') && s.captures.pendingCount === 0);
assert.deepEqual(pngSize(readFileSync(file('native-interactive'))), { width: 401, height: 201 });
run('identify', ['-regard-warnings', file('native-interactive')]);
await screenshot('niri-saved-history');
submitted = JSON.parse(ipc('capture', 'interactive', file('native-cancelled')));
const completions = snapshot().completions.length;
await delay(350);
await compositorKey('Escape');
await delay(350);
state = await check('native cancellation is unconfirmed, never fictitious saved history', s => s.captures.pendingCount === 1 && s.captures.recentCount === 3 && s.completions.length === completions);
assert(!existsSync(file('native-cancelled')));
assert.equal(state.captures.pending[0].id, submitted.lastAction.id);
ipc('capture', 'interactive', file('native-clipboard-only'));
await delay(350);
input('mousemove', '100', '100');
input('mousedown', '1');
input('mousemove', '500', '300');
input('mouseup', '1');
await compositorKey('ctrl+c');
await check('native clipboard-only null completion cannot consume a pending save', s => s.completions.at(-1) === null && s.captures.recentCount === 3 && s.captures.pendingCount === 2);
assert(!existsSync(file('native-clipboard-only')));
// wl-paste is a reader on the isolated compositor, never a second capture backend.
assert(run('wl-paste', ['--list-types']).includes('image/png'));
run('sh', ['-c', 'wl-paste --type image/png > /evidence/native-clipboard.png']);
assert.deepEqual(pngSize(readFileSync(file('native-clipboard'))), { width: 401, height: 201 });
run('identify', ['-regard-warnings', file('native-clipboard')]);
const unavailable = JSON.parse(ipc('unavailable'));
assert.equal(unavailable.lastAction.status, 'unavailable');
assert.equal(unavailable.pendingCount, 2);
assert(!existsSync(file('unavailable')));
await saved('focused-screen', 'native-repeat');
await saved('focused-screen', 'native-repeat');
state = snapshot();
assert.notEqual(state.captures.recent[0].id, state.captures.recent[1].id);
assert.equal(state.captures.pendingCount, 2);
await screenshot('niri-repeated-history');
// This single warning is required, not hidden: record its action-local log
// interval so the complete-log gate rejects missing, duplicate or stray warnings.
const nulls = state.completions.filter(path => path === null).length;
const nativeLog = join(evidence, 'niri.log');
const writeFailure = { start: readFileSync(nativeLog, 'utf8').length };
ipc('capture', 'focused-screen', '/evidence/missing-directory/native-failure.png');
await check('native failed write emits null and leaves pending/history honest', s => s.completions.filter(path => path === null).length > nulls && s.captures.pendingCount === 3 && s.captures.recentCount === 5 && readFileSync(nativeLog, 'utf8').slice(writeFailure.start).includes('error saving screenshot image: Os { code: 2, kind: NotFound, message: "No such file or directory" }\n'));
writeFailure.end = readFileSync(nativeLog, 'utf8').length;
assert(!existsSync('/evidence/missing-directory/native-failure.png'));
state = snapshot();
for (const record of state.captures.recent) {
assert(existsSync(record.path));
assert(state.text.includes(`${record.kind} · ${record.path}`));
}
assert(state.text.includes('saved: 5 · unconfirmed: 3'));
await screenshot('niri-unconfirmed-history');
submitted = JSON.parse(ipc('burst'));
assert.equal(submitted.recentCount, 5, 'a synchronous burst cannot fabricate completion');
assert.equal(submitted.pendingCount, 5);
await check('rapid native requests both dispatch without terminating one another', s => s.captures.recentCount === 7 && s.captures.pendingCount === 3);
for (const name of ['native-burst-screen', 'native-burst-window']) {
pngSize(readFileSync(file(name)));
run('identify', ['-regard-warnings', file(name)]);
}
submitted = JSON.parse(ipc('completeSelection', file('native-complete-selection')));
assert.equal(submitted.selection.phase, 'pending');
assert.equal(submitted.selection.completionFlash, false);
await delay(350);
input('mousemove', '100', '100');
input('mousedown', '1');
input('mousemove', '500', '300');
input('mouseup', '1');
await compositorKey('Return');
await check('completeSelection flashes only after its native save', s => s.captures.recentCount === 8 && s.captures.pendingCount === 3 && s.captures.selection.phase === 'completed' && s.captures.selection.completionFlash);
assert.deepEqual(pngSize(readFileSync(file('native-complete-selection'))), { width: 401, height: 201 });
run('identify', ['-regard-warnings', file('native-complete-selection')]);
await closeClient(beta);
await check('native close removes beta from the streamed window inventory', s => !s.niri.windows.some(row => row.id === beta.id) && s.niri.windowCount === 1);
action('focus-window', '--id', alpha.id);
await check('remaining window regains focus', s => s.niri.focusedWindow === alpha.id);
await closeClient(alpha);
await check('closing last native window clears title/app and retains one active pill', s => s.niri.windowCount === 0 && s.niri.focusedTitle === '' && s.niri.focusedAppId === '' && s.pills.length === 1);
ipc('clear');
await check('clear forgets saved and unconfirmed history visibly', s => s.captures.recentCount === 0 && s.captures.pendingCount === 0 && s.text.includes('saved: 0 · unconfirmed: 0'));
await screenshot('niri-cleared-history');
ipc('pasteFixture');
state = await check('synthetic paste destination gains native focus', s => s.niri.focusedTitle === 'paste destination' && s.paste.destinationFocused && s.paste.destinationEmpty);
const destinationId = state.niri.focusedWindow;
click(state.paste.chip);
state = await check('clipboard search owns popup focus before paste', s => s.paste.open && s.paste.searchFocused && s.paste.target === destinationId && s.paste.button);
input('type', 'focus-safe');
state = await check('clipboard destination search is retained', s => s.paste.queryPreserved && s.paste.destinationEmpty);
click(state.paste.button);
await check('pointer paste releases popup and types exactly once into destination', s => !s.paste.open && !s.paste.pending && s.paste.destinationMatches && s.paste.queryPreserved && s.niri.focusedWindow === destinationId && s.paste.error === '');
await screenshot('clipboard-paste-pointer');
key('ctrl+a');
key('BackSpace');
state = await check('destination editor remains keyboard operable after paste', s => s.paste.destinationEmpty);
click(state.paste.chip);
state = await check('clipboard reopen restores search before keyboard paste', s => s.paste.open && s.paste.searchFocused && s.paste.queryPreserved);
key('Return');
await check('keyboard paste returns focus and inserts exactly once', s => !s.paste.open && !s.paste.pending && s.paste.destinationMatches && s.paste.queryPreserved && s.niri.focusedWindow === destinationId && s.paste.error === '');
await screenshot('clipboard-paste-keyboard');
console.log('niri interaction assertions completed; complete runtime-log gate remains mandatory');
return writeFailure;
}