Files
omarchycn/test/shell.d/menu-plugin-test.sh
ff4e92e63b Fix Clone Plugin failing with "unknown clone option" (#6942)
omarchy-plugin-clone only takes the source id as the first argument, but the
menu passed --edit ahead of it, so the id fell through to the unknown-option
branch and every clone from Setup > Plugins failed.

Closes #6913

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 17:04:04 +02:00

186 lines
7.2 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
require_command jq
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
STUB_DIR="$TMPDIR/stub"
mkdir -p "$STUB_DIR"
# The picker reads the plugin list from omarchy-plugin-list and hands what it
# decided to a verb-specific command, so stubbing both ends shows which plugin
# a pick actually resolved to -- the thing a source-level check cannot see.
cat >"$STUB_DIR/omarchy-plugin-list" <<'STUB'
#!/bin/bash
cat "$FAKE_PLUGINS"
STUB
for command in omarchy-plugin-enable omarchy-plugin-disable; do
cat >"$STUB_DIR/$command" <<'STUB'
#!/bin/bash
printf '%s %s\n' "${0##*/}" "$*" >>"$FAKE_CALLS"
STUB
done
# Records the rows it was offered, then answers with the pick under test.
cat >"$STUB_DIR/omarchy-menu-select" <<'STUB'
#!/bin/bash
cat >"$FAKE_ROWS"
printf '%s\n' "$FAKE_PICK"
STUB
cat >"$STUB_DIR/omarchy-notification-send" <<'STUB'
#!/bin/bash
printf 'notification: %s\n' "$*" >>"$FAKE_CALLS"
STUB
cat >"$STUB_DIR/omarchy-launch-floating-terminal-with-presentation" <<'STUB'
#!/bin/bash
printf 'terminal: %s\n' "$*" >>"$FAKE_CALLS"
STUB
chmod +x "$STUB_DIR"/*
# Runs the picker against a plugin list, answering its prompt with $2. Leaves
# the rows it offered in $ROWS and what it called in $CALLS.
pick() {
local verb="$1" choice="$2"
: >"$TMPDIR/calls"
: >"$TMPDIR/rows"
HOME="$TMPDIR/home" \
PATH="$STUB_DIR:$PATH" \
FAKE_PLUGINS="$TMPDIR/plugins.json" \
FAKE_CALLS="$TMPDIR/calls" \
FAKE_ROWS="$TMPDIR/rows" \
FAKE_PICK="$choice" \
"$ROOT/bin/omarchy-menu-plugin" "$verb" >/dev/null 2>&1
ROWS=$(cat "$TMPDIR/rows")
CALLS=$(cat "$TMPDIR/calls")
}
# Two plugins can declare the same display name. The id rides along as row
# subtext and comes back with the selection, so the pick resolves by id.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": true},
{"id": "tester.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
]
JSON
pick enable "$(printf 'Clock\ttester.clock')"
[[ $ROWS == *"$(printf 'Clock\tomarchy.clock')"* && $ROWS == *"$(printf 'Clock\ttester.clock')"* ]] \
|| fail "picker offers the id as subtext on every row" "$ROWS"
pass "picker offers the id as subtext on every row"
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|| fail "picker acts on the row that was picked, not the one that shares its name" "$CALLS"
pass "picker acts on the row that was picked, not the one that shares its name"
pick remove "$(printf 'Clock\ttester.clock')"
[[ $CALLS == *"omarchy-plugin-remove tester.clock"* ]] \
|| fail "picker removes the plugin whose row was picked" "$CALLS"
pass "picker removes the plugin whose row was picked"
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "acme.weather", "name": "Weather", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
]
JSON
pick enable "$(printf 'Weather\tacme.weather')"
[[ $CALLS == *"omarchy-plugin-enable acme.weather"* ]] \
|| fail "picker delegates plugin enablement to the plugin command" "$CALLS"
pass "picker delegates plugin enablement to the plugin command"
# Clone offers only first-party plugins that no installed clone points back at,
# then hands the pick to the clone command, which opens the result in $EDITOR.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
{"id": "acme.weather", "name": "Weather", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
]
JSON
pick clone "$(printf 'Clock\tomarchy.clock')"
[[ $ROWS == *"Clock"* && $ROWS != *"Weather"* ]] ||
fail "clone picker offers only built-in plugins" "$ROWS"
pass "clone picker offers built-in plugins"
[[ $CALLS == *"terminal: omarchy-plugin-clone omarchy.clock --edit"* ]] ||
fail "clone picker delegates cloning and editing to the clone command" "$CALLS"
pass "clone picker clones and opens the personal plugin"
# Once a clone pointing back at the source is discovered, whatever it is named,
# the source no longer belongs in Clone.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
{"id": "tester.clock", "name": "My Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false, "clonedFrom": "omarchy.clock"}
]
JSON
pick clone ""
[[ $CALLS == *"notification: No plugin to clone"* ]] ||
fail "clone picker offers an already cloned plugin" "$CALLS"
pass "clone picker omits plugins already cloned locally"
# The picker treats every plugin alike and leaves kind-specific behavior to the
# plugin command.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "acme.fancy", "name": "Fancy", "kinds": ["bar", "bar-widget"], "enabled": false, "active": false, "canDisable": false, "firstParty": false}
]
JSON
pick enable "$(printf 'Fancy\tacme.fancy')"
[[ $CALLS == *"omarchy-plugin-enable acme.fancy"* && $CALLS != *"--section"* ]] \
|| fail "picker delegates kind-specific enablement" "$CALLS"
pass "picker delegates kind-specific enablement"
# A bar has no off, so it is never offered under disable -- including this one,
# which is a widget too.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "acme.fancy", "name": "Fancy", "kinds": ["bar", "bar-widget"], "enabled": true, "active": true, "canDisable": false, "firstParty": false},
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true}
]
JSON
pick disable "$(printf 'Clock\tomarchy.clock')"
[[ $ROWS == *"Clock"* && $ROWS != *"Fancy"* ]] \
|| fail "picker keeps a bar out of disable" "$ROWS"
pass "picker keeps a bar out of disable"
# The bar in use is the row absent from enable; every other bar is one pick away.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.bar", "name": "Bar", "kinds": ["bar"], "enabled": false, "active": false, "canDisable": false, "firstParty": true},
{"id": "tester.neon-bar", "name": "Neon Bar", "kinds": ["bar"], "enabled": true, "active": true, "canDisable": false, "firstParty": false}
]
JSON
pick enable "$(printf 'Bar\tomarchy.bar')"
[[ $ROWS == *"Bar"* && $ROWS != *"Neon Bar"* ]] \
|| fail "picker offers every bar except the one already running" "$ROWS"
pass "picker offers every bar except the one already running"
[[ $CALLS == *"omarchy-plugin-enable omarchy.bar"* ]] \
|| fail "picker returns to the built-in bar by enabling it" "$CALLS"
pass "picker returns to the built-in bar by enabling it"
# Nothing the verb can act on is said out loud, not opened as an empty list.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.bar", "name": "Bar", "kinds": ["bar"], "enabled": true, "active": true, "canDisable": false, "firstParty": true}
]
JSON
pick enable ""
[[ $CALLS == *"notification: No plugin to enable"* ]] \
|| fail "picker says when a verb has nothing to act on" "$CALLS"
pass "picker says when a verb has nothing to act on"