diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-21 10:10:19 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-21 10:10:19 +0200 |
| commit | 7416cef7233386a8cdeeee2977a693d3447eab51 (patch) | |
| tree | 2b135b47299041775d93f53ccf7f29be2a10fe03 /desktop/modules/sbbuilder/test_toml.js | |
| parent | 8938788f1f107dd72b1e2996a9b1e16238ff1820 (diff) | |
| download | quickshell-7416cef7233386a8cdeeee2977a693d3447eab51.tar.gz quickshell-7416cef7233386a8cdeeee2977a693d3447eab51.zip | |
sbbuilder: a line editor for sb-builder's TOML configs
Same shape as qtmaildir's Ini.js, so a file the user annotated keeps its
comments: unset comments a key out, set reactivates it. A value spanning
lines throws instead of gaining a duplicate key, which TOML rejects.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/sbbuilder/test_toml.js')
| -rw-r--r-- | desktop/modules/sbbuilder/test_toml.js | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/desktop/modules/sbbuilder/test_toml.js b/desktop/modules/sbbuilder/test_toml.js new file mode 100644 index 0000000..bb230e9 --- /dev/null +++ b/desktop/modules/sbbuilder/test_toml.js @@ -0,0 +1,95 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Run: node desktop/modules/sbbuilder/test_toml.js +// Toml.js is a QML library, so its .pragma line is stripped before node +// evaluates it. + +const fs = require("fs"); +const assert = require("assert"); + +const src = fs.readFileSync(__dirname + "/Toml.js", "utf8") + .replace(/^\.pragma library$/m, ""); +const T = new Function(src + "\nreturn { entries, get, set, remove };")(); + +const file = [ + "# helper config", + "# Every key is optional.", + "", + 'state_dir = "/var/lib/x"', + 'overlays = ["one", "two"] # searched in order', + "# upstream_branch = 'current'", + "", + "# bundled deps: package = its deps list URL", + "[manifests]", + 'neovim = "https://example.org/v{VERSION}/deps.txt"', + '"gtk+3" = "https://example.org/gtk"', + "", + "[refresh]", + "jobs = 5", + "on = true", + "", +].join("\n"); + +// Top-level keys are section "". +assert.strictEqual(T.get(file, "", "state_dir"), "/var/lib/x"); +assert.deepStrictEqual(T.get(file, "", "overlays"), ["one", "two"]); +assert.strictEqual(T.get(file, "", "upstream_branch"), undefined); +assert.strictEqual(T.get(file, "refresh", "jobs"), 5); +assert.strictEqual(T.get(file, "refresh", "on"), true); +// A quoted key reads unquoted; a commented line with = is not a key. +assert.deepStrictEqual(T.entries(file, "manifests").map(e => e.key), ["neovim", "gtk+3"]); +assert.deepStrictEqual(T.entries(file, "").map(e => e.key), ["state_dir", "overlays"]); + +// Replacing keeps the padding-free form, the inline comment and the line count. +let t = T.set(file, "", "overlays", ["a"]); +assert.strictEqual(t.split("\n")[4], 'overlays = ["a"] # searched in order'); +assert.strictEqual(t.split("\n").length, file.split("\n").length); +// Setting a commented key activates its line, single-quoted or not. +t = T.set(file, "", "upstream_branch", "main"); +assert.strictEqual(t.split("\n")[5], 'upstream_branch = "main"'); +// Unsetting comments a key out in place, and it reads back as unset. +t = T.set(file, "refresh", "jobs", null); +assert.strictEqual(t.split("\n")[13], "# jobs = 5"); +assert.strictEqual(T.get(t, "refresh", "jobs"), undefined); +// ...and setting it again reactivates that line. +assert.strictEqual(T.set(t, "refresh", "jobs", 9).split("\n")[13], "jobs = 9"); +// A new top-level key lands after the last top-level key, not after the +// [manifests] comment, which belongs to its section. +t = T.set(file, "", "genreprc", "/root/.genreprc"); +assert.strictEqual(t.split("\n")[5], 'genreprc = "/root/.genreprc"'); +assert.strictEqual(T.get(t, "", "genreprc"), "/root/.genreprc"); +// A new key in a file with only comments goes after the leading comments. +t = T.set("# head\n# more\n", "", "x", 1); +assert.strictEqual(t, "# head\n# more\nx = 1\n"); +// A new key in a section lands after that section's last key. +t = T.set(file, "manifests", "foo.bar", "u"); +assert.strictEqual(t.split("\n")[11], '"foo.bar" = "u"'); +assert.strictEqual(T.get(t, "manifests", "foo.bar"), "u"); +// A missing section is appended, and an empty file gets no blank lead. +t = T.set(file, "vm", "domain", "x"); +assert.ok(t.endsWith('\n\n[vm]\ndomain = "x"\n'), JSON.stringify(t)); +assert.strictEqual(T.set("", "vm", "autostart", false), "[vm]\nautostart = false\n"); +assert.strictEqual(T.set("", "", "target", "h"), 'target = "h"\n'); +// Strings are escaped and read back the same. +t = T.set(file, "", "state_dir", 'a "b" \\ c'); +assert.strictEqual(T.get(t, "", "state_dir"), 'a "b" \\ c'); +// The same key in another section is untouched. +assert.strictEqual(T.set("[a]\nx = 1\n\n[b]\nx = 2\n", "b", "x", 3), "[a]\nx = 1\n\n[b]\nx = 3\n"); +// A value spanning lines is refused, never duplicated. +assert.throws(() => T.set("o = [\n \"a\",\n]\n", "", "o", ["b"]), /cannot edit/); + +// remove deletes the line outright. +t = T.remove(file, "manifests", "gtk+3"); +assert.deepStrictEqual(T.entries(t, "manifests").map(e => e.key), ["neovim"]); +assert.strictEqual(T.remove(file, "manifests", "nope"), file); + +console.log("test_toml: ok"); |
