Files
omarchycn/test/shell.d/bluetooth-test.sh
T
David Heinemeier HanssonandClaude Fable 5 f54edbeba8 Expose the shell's remaining UI-only toggles over IPC
Every toggle you could only reach by mouse or panel hotkey is now
scriptable through omarchy-shell:

- omarchy.bluetooth toggleBluetooth — the radio (was right-click / B)
- omarchy.network toggleNetwork — Wi-Fi on/off (was W in the panel)
- omarchy.tailscale toggleTailscale — the connection as one call,
  matching the widget's right-click, instead of separate up/down
- omarchy.clock toggleWeekStart — calendar week start (was W)
- shell toggleBarTransparency — the bar background (was manage UI only)

Bluetooth and network follow the power/monitor pattern: manageIpc: false
so the panel owns the target's single IpcHandler and can extend it past
the inherited lifecycle methods.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01 15:44:07 -05:00

141 lines
5.9 KiB
Bash

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
grep -q '^ConditionPathIsDirectory=/sys/class/bluetooth$' "$ROOT/default/systemd/user/bt-agent.service" || \
fail "bt-agent is skipped on machines without Bluetooth hardware"
pass "bt-agent is skipped on machines without Bluetooth hardware"
run_node_test <<'JS'
const fs = require('fs')
const bluetooth = requireFromRoot('shell/plugins/panels/bluetooth/Model.js')
const panelSource = fs.readFileSync(root + '/shell/plugins/panels/bluetooth/Panel.qml', 'utf8')
assert(/IpcHandler[\s\S]*?function toggleBluetooth\(\) \{ root\.toggleBluetooth\(\) \}/.test(panelSource), 'bluetooth exposes the radio toggle over IPC')
assert(/manageIpc: false/.test(panelSource), 'bluetooth owns its IPC handler so it can extend the target methods')
assert(bluetooth.isUuidLike('0000110b-0000-1000-8000-00805f9b34fb'), 'bluetooth detects UUID-like names')
assert(bluetooth.isAddressLike('AA:BB:CC:DD:EE:FF'), 'bluetooth detects address-like names')
assertEqual(bluetooth.normalizedAddress('AA:BB_CC-dd-ee-ff'), 'aabbccddeeff', 'bluetooth normalizes BlueZ and PipeWire address formats')
assert(!bluetooth.hasHumanName({ name: 'AA:BB:CC:DD:EE:FF' }), 'bluetooth rejects address-only device labels')
assert(bluetooth.hasHumanName({ deviceName: 'MX Master 3S' }), 'bluetooth accepts human device labels')
const devices = [
{ name: 'Speaker', connected: false, paired: true, address: '2' },
{ name: 'Headphones', connected: true, address: '1' },
{ name: 'Keyboard', connected: false, address: '3' },
{ name: 'AA:BB:CC:DD:EE:FF', connected: true, address: '4' },
{ name: 'Mouse', connected: false, trusted: true, address: '5' }
]
const arrayLikeDevices = {
0: devices[0],
1: devices[1],
length: 2
}
assertDeepEqual(
bluetooth.toArray(arrayLikeDevices).map(bluetooth.deviceLabel),
['Speaker', 'Headphones'],
'bluetooth converts Quickshell QObjectList-style values into arrays'
)
const lists = bluetooth.deviceLists(devices)
assertDeepEqual(lists.connected.map(bluetooth.deviceLabel), ['Headphones'], 'bluetooth groups connected devices')
assertDeepEqual(lists.known.map(bluetooth.deviceLabel), ['Mouse', 'Speaker'], 'bluetooth groups known devices by label')
assertDeepEqual(lists.discovered.map(bluetooth.deviceLabel), ['Keyboard'], 'bluetooth groups discovered devices')
assertDeepEqual(bluetooth.visibleSections(lists, true), ['connected', 'known', 'discovered'], 'bluetooth shows discovered section while scanning')
assertDeepEqual(bluetooth.visibleSections(lists, false), ['connected', 'known'], 'bluetooth hides discovered section when not scanning')
const arrayLikeLists = bluetooth.deviceLists({
0: { name: 'Earbuds', connected: true, address: '6' },
1: { name: 'Trackpad', paired: true, address: '7' },
2: { name: 'Gamepad', address: '8' },
length: 3
})
assertDeepEqual(arrayLikeLists.connected.map(bluetooth.deviceLabel), ['Earbuds'], 'bluetooth groups connected devices from array-like values')
assertDeepEqual(arrayLikeLists.known.map(bluetooth.deviceLabel), ['Trackpad'], 'bluetooth groups known devices from array-like values')
assertDeepEqual(arrayLikeLists.discovered.map(bluetooth.deviceLabel), ['Gamepad'], 'bluetooth groups discovered devices from array-like values')
assertDeepEqual(
bluetooth.withPendingAction({ a: 'connecting' }, 'b', 'forgetting'),
{ a: 'connecting', b: 'forgetting' },
'bluetooth adds pending actions immutably'
)
assertDeepEqual(bluetooth.withPendingAction({ a: 'connecting' }, 'a', ''), {}, 'bluetooth clears pending actions immutably')
const bluetoothSink = {
isSink: true,
isStream: false,
ready: true,
name: 'bluez_output.AA_BB_CC_DD_EE_FF.1',
properties: {
'device.product.name': 'JBL Go 3'
}
}
assert(
bluetooth.bluetoothSinkMatchesDevice(bluetoothSink, { address: 'AA:BB:CC:DD:EE:FF', name: 'JBL Go 3' }),
'bluetooth matches audio sinks by device address'
)
assert(
bluetooth.bluetoothSinkMatchesDevice(
{
isSink: true,
isStream: false,
ready: true,
name: 'alsa_output.usb-speaker',
properties: { 'device.product.name': 'JBL Go 3' }
},
{ address: '11:22:33:44:55:66', name: 'JBL Go 3' }
),
'bluetooth matches audio sinks by human device label when address is unavailable'
)
assert(
!bluetooth.bluetoothSinkMatchesDevice({ isSink: false, isStream: false, ready: true, name: 'bluez_output.AA_BB_CC_DD_EE_FF.1', properties: {} }, { address: 'AA:BB:CC:DD:EE:FF', name: 'JBL Go 3' }),
'bluetooth ignores non-sink nodes when matching audio outputs'
)
JS
# The power-on shortcut is the whole point of skipping the stabilization sleep:
# pair/connect from the panel run against an adapter that is already powered.
device_tmp=$(mktemp -d)
trap 'rm -rf "$device_tmp"' EXIT
mock_bin="$device_tmp/bin"
mkdir -p "$mock_bin"
cat >"$mock_bin/bluetoothctl" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >>"$BLUETOOTHCTL_LOG"
[[ $1 == "show" ]] && printf '\tPowered: %s\n' "$BLUETOOTHCTL_POWERED"
exit 0
SH
chmod +x "$mock_bin/bluetoothctl"
bluetooth_device_log() {
local powered="$1"
local log="$device_tmp/$powered.log"
: >"$log"
PATH="$mock_bin:$PATH" BLUETOOTHCTL_LOG="$log" BLUETOOTHCTL_POWERED="$powered" \
"$ROOT/bin/omarchy-bluetooth-device" connect AA:BB:CC:DD:EE:FF ||
fail "omarchy-bluetooth-device exits cleanly with Powered: $powered"
printf '%s' "$log"
}
powered_log=$(bluetooth_device_log yes)
grep -qx "power on" "$powered_log" &&
fail "bluetooth skips the power-on delay when the adapter is already powered"
pass "bluetooth skips the power-on delay when the adapter is already powered"
grep -qx "connect AA:BB:CC:DD:EE:FF" "$powered_log" ||
fail "bluetooth still connects when the adapter is already powered"
pass "bluetooth still connects when the adapter is already powered"
unpowered_log=$(bluetooth_device_log no)
grep -qx "power on" "$unpowered_log" ||
fail "bluetooth powers the adapter on when it is off"
pass "bluetooth powers the adapter on when it is off"