aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-20 21:55:13 -0400
committerCraig Jennings <c@cjennings.net>2026-05-20 21:55:13 -0400
commit62e45197bb4d440e002b731715343e565cf50ff3 (patch)
tree4e1200983cfb953763c39154c5c8731dd2a66648 /tests
parentcb209a2d01f5c17024738b490c8fa109959b5303 (diff)
downloadarchsetup-62e45197bb4d440e002b731715343e565cf50ff3.tar.gz
archsetup-62e45197bb4d440e002b731715343e565cf50ff3.zip
feat(hyprland): add touchpad state indicator to waybar
The $mod+F9 toggle and the toggle-touchpad / touchpad-auto scripts already worked, but the scripts lived only in ~/.local/bin and were never committed, and there was no way to see the touchpad's state at a glance. I committed both scripts into the repo so stow installs them, and added a waybar indicator: a waybar-touchpad status script and a custom/touchpad module that shows a mouse glyph when the touchpad is on and a mouse-off glyph (in the dupre orange) when it's off. The scripts signal the module with pkill -RTMIN+9 waybar after each state change, so the icon updates the moment the touchpad toggles. touchpad-auto now runs at login via exec-once. The waybar-touchpad script has unit tests under tests/waybar-touchpad/ covering the enabled, disabled, and missing-state-file cases.
Diffstat (limited to 'tests')
-rw-r--r--tests/waybar-touchpad/test_waybar_touchpad.py104
1 files changed, 104 insertions, 0 deletions
diff --git a/tests/waybar-touchpad/test_waybar_touchpad.py b/tests/waybar-touchpad/test_waybar_touchpad.py
new file mode 100644
index 0000000..3133bb4
--- /dev/null
+++ b/tests/waybar-touchpad/test_waybar_touchpad.py
@@ -0,0 +1,104 @@
+"""Tests for dotfiles/hyprland/.local/bin/waybar-touchpad.
+
+The script emits one JSON line for waybar's custom/touchpad module, derived
+from the touchpad state file that toggle-touchpad / touchpad-auto maintain
+at $XDG_RUNTIME_DIR/touchpad-state. Tests point XDG_RUNTIME_DIR at a temp
+dir and assert on the emitted JSON for each state — enabled, disabled, and
+the missing-file default. No mocking: the real script runs against a real
+state file.
+
+Run from repo root:
+ python3 -m unittest tests.waybar-touchpad.test_waybar_touchpad
+"""
+
+import json
+import os
+import shutil
+import subprocess
+import tempfile
+import unittest
+
+
+REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
+SCRIPT = os.path.join(REPO_ROOT, "dotfiles/hyprland/.local/bin/waybar-touchpad")
+
+
+class WaybarTouchpadHarness(unittest.TestCase):
+
+ def setUp(self):
+ self.tmp = tempfile.mkdtemp(prefix="waybar-touchpad-test-")
+ self.state_file = os.path.join(self.tmp, "touchpad-state")
+
+ def tearDown(self):
+ shutil.rmtree(self.tmp, ignore_errors=True)
+
+ def set_state(self, value):
+ with open(self.state_file, "w") as f:
+ f.write(value)
+
+ def run_script(self):
+ env = os.environ.copy()
+ env["XDG_RUNTIME_DIR"] = self.tmp
+ return subprocess.run(
+ [SCRIPT], env=env, capture_output=True, text=True, timeout=10,
+ )
+
+ def emitted_json(self):
+ result = self.run_script()
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ return json.loads(result.stdout)
+
+
+# -----------------------------------------------------------------------------
+# Normal cases
+# -----------------------------------------------------------------------------
+
+class TestWaybarTouchpadNormal(WaybarTouchpadHarness):
+
+ def test_enabled_state_emits_enabled_class_and_mouse_icon(self):
+ self.set_state("enabled")
+ data = self.emitted_json()
+ self.assertEqual(data["class"], "enabled")
+ self.assertIn("\U000f037d", data["text"]) # 󰍽 mouse
+ self.assertIn("enabled", data["tooltip"].lower())
+
+ def test_disabled_state_emits_disabled_class_and_mouse_off_icon(self):
+ self.set_state("disabled")
+ data = self.emitted_json()
+ self.assertEqual(data["class"], "disabled")
+ self.assertIn("\U000f037e", data["text"]) # 󰍾 mouse-off
+ self.assertIn("disabled", data["tooltip"].lower())
+
+
+# -----------------------------------------------------------------------------
+# Boundary cases
+# -----------------------------------------------------------------------------
+
+class TestWaybarTouchpadBoundary(WaybarTouchpadHarness):
+
+ def test_missing_state_file_defaults_to_enabled(self):
+ # No state file written → script should treat the touchpad as enabled.
+ data = self.emitted_json()
+ self.assertEqual(data["class"], "enabled")
+
+ def test_state_with_trailing_newline_is_handled(self):
+ self.set_state("disabled\n")
+ data = self.emitted_json()
+ self.assertEqual(data["class"], "disabled")
+
+ def test_unknown_state_value_treated_as_enabled(self):
+ # Anything that isn't "disabled" reads as enabled (fail-safe: the
+ # pointer is usable rather than hidden).
+ self.set_state("garbage")
+ data = self.emitted_json()
+ self.assertEqual(data["class"], "enabled")
+
+ def test_output_is_a_single_json_object(self):
+ self.set_state("enabled")
+ result = self.run_script()
+ lines = [ln for ln in result.stdout.splitlines() if ln.strip()]
+ self.assertEqual(len(lines), 1, msg=f"expected one line, got {lines!r}")
+
+
+if __name__ == "__main__":
+ unittest.main()