import assert from "node:assert/strict"; import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import test from "node:test"; const niriDir = dirname(fileURLToPath(import.meta.url)); const root = resolve(niriDir, ".."); const justArgs = ["--shell", "nu", "--clear-shell-args", "--shell-arg=--no-config-file", "--shell-arg=--commands", "niri"]; function run(command, args, cwd) { const result = spawnSync(command, args, { cwd, encoding: "utf8" }); assert.equal(result.status, 0, `${command}: ${result.error ?? ""}\n${result.stdout}\n${result.stderr}`); return result.stdout; } test("Nugu installs both Niri themes; config alone selects the active variant", () => { const workspace = mkdtempSync(join(tmpdir(), "niri-theme-test-")); try { const nugu = join(workspace, "nugu"); const config = join(workspace, "niri"); mkdirSync(nugu); // Only source inputs: no pre-generated palettes or compositor intermediaries. for (const file of ["base.toml", "nugu.nu", "niri.nu", "justfile"]) cpSync(join(root, "nugu", file), join(nugu, file)); run("just", justArgs, nugu); assert.equal(existsSync(join(config, "config.kdl")), false, "generation must not select an active theme"); cpSync(join(niriDir, "cfg"), join(config, "cfg"), { recursive: true }); const originalConfig = readFileSync(join(niriDir, "config.kdl"), "utf8"); const include = /^include "themes\/nugu-(?:dark|light)\.kdl"$/gm; assert.equal([...originalConfig.matchAll(include)].length, 1, "config must select exactly one theme"); const originalLayout = readFileSync(join(config, "cfg/layout.kdl"), "utf8"); const originalRules = readFileSync(join(config, "cfg/rules.kdl"), "utf8"); const generated = new Set(); for (const mode of ["dark", "light"]) { const p = JSON.parse(run("nu", ["--no-config-file", "-c", `use nugu.nu; nugu ${mode} | get palette | to json`], nugu)); const filename = `nugu-${mode}.kdl`; const kdl = readFileSync(join(nugu, "output/niri", filename), "utf8"); assert.equal(readFileSync(join(config, "themes", filename), "utf8"), kdl); assert.equal(readFileSync(join(niriDir, "themes", filename), "utf8"), kdl, "repo theme must match generator output"); for (const block of ["focus-ring", "tab-indicator", "highlight"]) { const body = kdl.match(new RegExp(`${block} \\{([^}]+)\\}`))?.[1]; assert.ok(body, block); assert.ok(body.includes(`active-color "${p.ui.focus}"`), block); assert.ok(body.includes(`urgent-color "${p.error}"`), block); if (block !== "highlight") assert.ok(body.includes(`inactive-color "${p.ui.unfocus}"`), block); } assert.match(kdl, new RegExp(`insert-hint \\{\\s*color "${p.ui.focus}80"\\s*\\}`)); assert.match(kdl, new RegExp(`overview \\{\\s*backdrop-color "${p.ui.backdrop}"\\s*\\}`)); assert.doesNotMatch(kdl, /\b(?:background-color|shadow|border|on|off|width|gap|binds)\b/); const selectedConfig = originalConfig.replace(include, `include "themes/${filename}"`); writeFileSync(join(config, "config.kdl"), selectedConfig); run("just", justArgs, nugu); assert.equal(readFileSync(join(config, "config.kdl"), "utf8"), selectedConfig, "regeneration must preserve theme selection"); assert.equal(readFileSync(join(config, "cfg/layout.kdl"), "utf8"), originalLayout); assert.equal(readFileSync(join(config, "cfg/rules.kdl"), "utf8"), originalRules); assert.equal(existsSync(join(config, "themes", `${filename}.tmp`)), false); run("niri", ["validate", "-c", join(config, "config.kdl")], workspace); generated.add(kdl); } assert.equal(generated.size, 2, "dark and light must differ"); const invalid = spawnSync("nu", ["--no-config-file", "-c", "use niri.nu; niri theme invalid"], { cwd: nugu, encoding: "utf8" }); assert.notEqual(invalid.status, 0); assert.match(invalid.stderr, /Expected dark or light/); assert.equal(existsSync(join(config, "cfg/nugu.kdl")), false, "no copied active-theme intermediary"); assert.equal(existsSync(join(nugu, "output/dark.toml")), false); assert.equal(existsSync(join(nugu, "output/light.toml")), false); assert.equal(existsSync(join(workspace, "hypr")), false); } finally { rmSync(workspace, { recursive: true, force: true }); } });