95 lines
4.0 KiB
Bash
Executable File
95 lines
4.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
service="$repo_dir/config/dot/quickshell/services/Notifs.qml"
|
|
page="$repo_dir/config/dot/quickshell/modules/settings/NotificationsPage.qml"
|
|
|
|
fail() {
|
|
printf 'notification application rules contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
[[ -f "$service" ]] || fail 'notification service is missing'
|
|
[[ -f "$page" ]] || fail 'notification settings page is missing'
|
|
|
|
SERVICE_PATH="$service" PAGE_PATH="$page" bun -e '
|
|
const source = await Bun.file(process.env.SERVICE_PATH).text();
|
|
const page = await Bun.file(process.env.PAGE_PATH).text();
|
|
|
|
function fail(message) {
|
|
console.error(`notification application rules contract: ${message}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function functionBody(name) {
|
|
const start = source.indexOf(`function ${name}(`);
|
|
if (start === -1)
|
|
fail(`missing ${name}()`);
|
|
const open = source.indexOf("{", start);
|
|
let depth = 0;
|
|
for (let index = open; index < source.length; index++) {
|
|
if (source[index] === "{") depth++;
|
|
if (source[index] === "}" && --depth === 0)
|
|
return source.slice(open + 1, index);
|
|
}
|
|
fail(`${name}() is unterminated`);
|
|
}
|
|
|
|
const notificationAppId = Function("notification", functionBody("notificationAppId"));
|
|
const normalizedAppRule = Function("rule", functionBody("normalizedAppRule"));
|
|
|
|
const identityFixtures = [
|
|
{ notification: { desktopEntry: "org.signal.Signal.desktop", appName: "Signal" }, expected: "org.signal.Signal.desktop" },
|
|
{ notification: { desktopEntry: "", appName: "Terminal" }, expected: "Terminal" },
|
|
{ notification: { desktopEntry: "", appName: "" }, expected: "Notifications" }
|
|
];
|
|
for (const fixture of identityFixtures) {
|
|
const actual = notificationAppId(fixture.notification);
|
|
if (actual !== fixture.expected)
|
|
fail(`stable app identity expected ${fixture.expected}, got ${actual}`);
|
|
}
|
|
|
|
const defaultRule = normalizedAppRule({});
|
|
if (JSON.stringify(defaultRule) !== JSON.stringify({ enabled: true, showOnLockScreen: true, showContentOnLockScreen: true }))
|
|
fail(`missing rule fields did not default safely: ${JSON.stringify(defaultRule)}`);
|
|
|
|
const explicitRule = normalizedAppRule({ enabled: false, showOnLockScreen: false, showContentOnLockScreen: false });
|
|
if (JSON.stringify(explicitRule) !== JSON.stringify({ enabled: false, showOnLockScreen: false, showContentOnLockScreen: false }))
|
|
fail(`explicit rule was not preserved: ${JSON.stringify(explicitRule)}`);
|
|
|
|
for (const required of ["rememberApplication", "appRule", "setAppRule", "shouldShowOnLockScreen", "shouldShowContentOnLockScreen"]) {
|
|
functionBody(required);
|
|
}
|
|
|
|
const arrival = source.indexOf("onNotification: notification =>");
|
|
const tracked = source.indexOf("notification.tracked = true", arrival);
|
|
const muted = source.indexOf("!root.appRule(appId).enabled", arrival);
|
|
if (arrival === -1 || tracked === -1 || muted === -1 || muted > tracked)
|
|
fail("muted applications are not rejected before tracking/history/unread/toast work");
|
|
if (!source.includes("DesktopPreferences.get(\"notificationAppRules\")"))
|
|
fail("rules are not read through DesktopPreferences");
|
|
if (!source.includes("DesktopPreferences.set(\"notificationAppRules\", next)"))
|
|
fail("rules are not written through DesktopPreferences");
|
|
if (!source.includes("next[knownAppId] = root.appRule(knownAppId)"))
|
|
fail("persisted rules are not normalized to the required three-field shape");
|
|
if (!source.includes("root.fallbackAppRules = next"))
|
|
fail("missing-schema preference writes do not retain an in-memory fallback");
|
|
if (!source.includes("if (!root.doNotDisturb)"))
|
|
fail("global DND popup override was removed");
|
|
|
|
for (const required of [
|
|
"Notifs.applications",
|
|
"Notifs.appRule(app.id).enabled",
|
|
"showOnLockScreen",
|
|
"showContentOnLockScreen",
|
|
"Notifs.setAppRule"
|
|
]) {
|
|
if (!page.includes(required))
|
|
fail(`settings page is missing ${required}`);
|
|
}
|
|
|
|
console.log("notification application rules contract: PASS");
|
|
'
|