127 lines
4.6 KiB
Bash
Executable File
127 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
fail() {
|
|
printf 'applications settings contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
page="$project_root/config/dot/quickshell/modules/settings/ApplicationsPage.qml"
|
|
|
|
[[ -f "$page" ]] || fail 'Applications page is missing'
|
|
|
|
assert_contains() {
|
|
rg -F --quiet "$1" "$page" || fail "page is missing: $1"
|
|
}
|
|
|
|
assert_contains 'SettingsPage {'
|
|
assert_contains 'objectName: "applications"'
|
|
assert_contains 'DesktopEntries.applications.values'
|
|
assert_contains 'DefaultApps'
|
|
assert_contains 'SettingsCard {'
|
|
assert_contains 'SettingRow {'
|
|
assert_contains 'activatable:'
|
|
assert_contains 'ActionRow {'
|
|
assert_contains 'TextRow {'
|
|
|
|
for label in Browser Mail Files Terminal Music Images Video; do
|
|
assert_contains "label: \"$label\""
|
|
done
|
|
|
|
assert_contains 'title: "Default applications"'
|
|
assert_contains 'title: "User autostart"'
|
|
assert_contains 'title: "Compositor autostart"'
|
|
assert_contains 'categories'
|
|
assert_contains 'genericName'
|
|
assert_contains '.sort('
|
|
assert_contains 'currentEntry'
|
|
assert_contains 'read-only'
|
|
assert_contains 'choices.push(currentEntry)'
|
|
assert_contains 'label: "Application settings need attention"'
|
|
assert_contains 'DefaultApps.busy ? "Loading…"'
|
|
assert_contains 'visible: !DefaultApps.busy && DefaultApps.autostartEntries.length === 0'
|
|
assert_contains 'visible: !DefaultApps.busy && DefaultApps.luaAutostartEntries.length === 0'
|
|
|
|
PAGE_PATH="$page" bun -e '
|
|
const source = await Bun.file(process.env.PAGE_PATH).text();
|
|
const rolesSource = source.match(/readonly property var roles:\s*(\[[\s\S]*?\n \])/);
|
|
const matcherSource = source.match(/function matchesRole\(entry: var, role: var\): bool \{([\s\S]*?)\n \}/);
|
|
if (!rolesSource || !matcherSource) {
|
|
console.error("applications settings contract: role matcher could not be loaded");
|
|
process.exit(1);
|
|
}
|
|
|
|
const roles = Function(`return (${rolesSource[1]})`)();
|
|
const matchesRole = Function("entry", "role", matcherSource[1]);
|
|
const role = key => roles.find(candidate => candidate.key === key);
|
|
const fixtures = [
|
|
{
|
|
name: "AudioVideo does not imply music",
|
|
entry: { name: "Kodi", genericName: "Media Center", comment: "Entertainment hub", categories: "AudioVideo;Player;" },
|
|
role: "music",
|
|
expected: false
|
|
},
|
|
{
|
|
name: "Graphics does not imply image handler",
|
|
entry: { name: "Document Scanner", genericName: "Document Scanner", comment: "Scan documents", categories: ["Graphics"] },
|
|
role: "images",
|
|
expected: false
|
|
},
|
|
{
|
|
name: "Viewer does not imply image handler",
|
|
entry: { name: "Papers", genericName: "Document Viewer", comment: "Read documents", categories: "Office;Viewer;" },
|
|
role: "images",
|
|
expected: false
|
|
},
|
|
{
|
|
name: "comment does not nominate a default handler",
|
|
entry: { name: "Settings", genericName: "System Settings", comment: "Configure your video player", categories: ["System"] },
|
|
role: "video",
|
|
expected: false
|
|
},
|
|
{
|
|
name: "exact audio player categories match music",
|
|
entry: { name: "Rhythmbox", genericName: "Music Player", comment: "Play music", categories: "AudioVideo;Audio;Player;" },
|
|
role: "music",
|
|
expected: true
|
|
},
|
|
{
|
|
name: "exact video category matches video",
|
|
entry: { name: "Videos", genericName: "Video Player", comment: "Play movies", categories: ["AudioVideo", "Video", "Player"] },
|
|
role: "video",
|
|
expected: true
|
|
},
|
|
{
|
|
name: "descriptive metadata matches image handler",
|
|
entry: { name: "Loupe", genericName: "Image Viewer", comment: "Browse pictures", categories: "Graphics;Viewer;" },
|
|
role: "images",
|
|
expected: true
|
|
}
|
|
];
|
|
|
|
for (const fixture of fixtures) {
|
|
const actual = matchesRole(fixture.entry, role(fixture.role));
|
|
if (actual !== fixture.expected) {
|
|
console.error(`applications settings contract: ${fixture.name}: expected ${fixture.expected}, got ${actual}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
'
|
|
|
|
if rg --quiet 'Component\.onCompleted|DesktopEntries\.(byId|heuristicLookup)' "$page"; then
|
|
fail 'page snapshots or performs a one-time desktop-entry lookup'
|
|
fi
|
|
if rg -F --quiet 'label: "Could not apply the change"' "$page"; then
|
|
fail 'error heading incorrectly describes read failures as apply failures'
|
|
fi
|
|
if rg --quiet '#[0-9A-Fa-f]{3,8}' "$page"; then
|
|
fail 'page introduces a color literal instead of the shared visual system'
|
|
fi
|
|
|
|
[[ "$(rg --count 'activatable:' "$page")" -ge 2 ]] \
|
|
|| fail 'default and autostart rows are not both whole-row activatable'
|
|
|
|
printf 'applications settings contract: PASS\n'
|