diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/fixtures/panel-lifecycle.js | 112 | ||||
| -rw-r--r-- | tests/test_canonical.py | 2 | ||||
| -rw-r--r-- | tests/test_canonical_desktop.py | 24 | ||||
| -rw-r--r-- | tests/test_canonical_panel.py | 89 |
4 files changed, 227 insertions, 0 deletions
diff --git a/tests/fixtures/panel-lifecycle.js b/tests/fixtures/panel-lifecycle.js new file mode 100644 index 0000000..d035289 --- /dev/null +++ b/tests/fixtures/panel-lifecycle.js @@ -0,0 +1,112 @@ +import assert from "node:assert/strict"; + +const callbacks = []; +let killed = 0; +let removed = 0; +class Actor { + constructor(props = {}) { + Object.assign(this, props); + } + add_child() {} + set_text(text) { + this.text = text; + } + set_style_class_name(name) { + this.style = name; + } + destroy() { + this.destroyed = true; + } +} +class Button extends Actor { + menu = { addMenuItem() {}, addAction() {} }; +} +class Item { + label = new Actor(); +} +class Extension { + uuid = "test"; +} +const St = { BoxLayout: Actor, Label: Actor }; +const Clutter = { ActorAlign: { CENTER: 0 } }; +const PanelMenu = { Button }; +const PopupMenu = { PopupMenuItem: Item }; +const Main = { panel: { addToStatusArea() {} }, notifyError() {} }; +const GLib = { + PRIORITY_DEFAULT: 0, + SOURCE_CONTINUE: true, + timeout_add_seconds: () => 1, + Source: { + remove() { + removed++; + }, + }, + get_home_dir: () => "/home/test", + build_filenamev: (parts) => parts.join("/"), +}; +const Gio = { + Cancellable: class { + cancelled = false; + cancel() { + this.cancelled = true; + } + is_cancelled() { + return this.cancelled; + } + }, + SubprocessFlags: { NONE: 0, STDOUT_PIPE: 1, STDERR_PIPE: 2 }, + Subprocess: { + new() { + return { + communicate_utf8_async(_input, _token, callback) { + callbacks.push(callback); + }, + force_exit() { + killed++; + }, + }; + }, + }, +}; + +// EXTENSION + +const panel = new CorporatePanel(); +panel.enable(); +panel._refresh(); +assert.equal(callbacks.length, 1, "Do not overlap status processes"); +panel._render({ + displays: "EXT 1", + updates: "APT 3", + failed: "FAIL 1", + reboot: "", + errors: "", +}); +assert.equal(panel._labels.failed.style, "corporate-critical"); +assert.equal(panel._labels.updates.style, "corporate-warning"); +assert.equal(panel._labels.reboot.visible, false); +const button = panel._button; +panel.disable(); +assert.equal(killed, 1); +assert.equal(removed, 1); +assert.equal(button.destroyed, true); +panel.enable(); +callbacks[0]({}, {}); +assert.ok(panel._process, "Old callbacks cannot clear the new process"); +const data = { + displays: "EXT 0", + updates: "APT 0", + failed: "FAIL 0", + reboot: "REBOOT", + errors: "", +}; +callbacks[1]( + { + communicate_utf8_finish: () => [true, JSON.stringify(data), ""], + get_successful: () => true, + }, + {}, +); +assert.equal(panel._labels.reboot.visible, true); +assert.equal(panel._process, null); +panel.disable(); diff --git a/tests/test_canonical.py b/tests/test_canonical.py index e1c825c..27fd199 100644 --- a/tests/test_canonical.py +++ b/tests/test_canonical.py @@ -132,6 +132,7 @@ class RoleTests(unittest.TestCase): ".config/git/config", ".config/ghostty/config", ".local/bin/rqr", + ".local/share/gnome-shell/extensions/corporate-panel@dotfiles/extension.js", ]: self.assertIn(required, files) for path in files: @@ -203,6 +204,7 @@ class RoleTests(unittest.TestCase): ) self.assertNotIn(".config/autostart/dotfiles-", files) self.assertNotIn("gpg-agent.service.d/canonical.conf", files) + self.assertNotIn("corporate-panel@dotfiles", files) if __name__ == "__main__": diff --git a/tests/test_canonical_desktop.py b/tests/test_canonical_desktop.py index cdbc812..9bdace1 100644 --- a/tests/test_canonical_desktop.py +++ b/tests/test_canonical_desktop.py @@ -14,6 +14,30 @@ SPEC.loader.exec_module(desktop) class DesktopTests(unittest.TestCase): + def test_clipboard_shortcut_preserves_other_display_bindings(self): + settings = MagicMock() + settings.get_strv.return_value = ["<Super>p", "XF86Display", "<Super>x"] + with ( + patch.object(desktop, "settings_object", return_value=settings), + patch.object(desktop, "write_key") as write, + ): + desktop.shortcuts({}) + write.assert_any_call( + "org.gnome.mutter.keybindings", + "switch-monitor", + ["XF86Display", "<Super>x"], + {}, + ) + + def test_panel_settings_disable_external_ip_lookup(self): + with patch.object(desktop, "write_key") as write: + desktop.panel({}) + write.assert_any_call( + "org.gnome.shell.extensions.vitals", "include-public-ip", False, {} + ) + self.assertIn("corporate-panel@dotfiles", desktop.EXTENSIONS) + self.assertIn("ubuntu-appindicators@ubuntu.com", desktop.EXTENSIONS) + def test_unknown_keys_fail_without_writing(self): settings = MagicMock() settings.props.settings_schema.list_keys.return_value = [] diff --git a/tests/test_canonical_panel.py b/tests/test_canonical_panel.py new file mode 100644 index 0000000..efdf7a5 --- /dev/null +++ b/tests/test_canonical_panel.py @@ -0,0 +1,89 @@ +import importlib.util +import json +import subprocess +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +ROOT = Path(__file__).resolve().parents[1] +SPEC = importlib.util.spec_from_file_location( + "panel", ROOT / "dot_local/lib/dotfiles/canonical_panel.py" +) +assert SPEC and SPEC.loader +panel = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(panel) + + +class PanelTests(unittest.TestCase): + def test_extension_lifecycle(self): + source = ( + ROOT + / "dot_local/share/gnome-shell/extensions/corporate-panel@dotfiles/extension.js" + ) + code = "\n".join( + line + for line in source.read_text().splitlines() + if not line.startswith("import ") + ) + code = code.replace("export default class", "class") + harness = (ROOT / "tests/fixtures/panel-lifecycle.js").read_text() + subprocess.run( + ["node", "--input-type=module"], + input=harness.replace("// EXTENSION", code), + text=True, + check=True, + ) + + def test_failed_units_use_both_scopes(self): + with patch.object( + panel, "output", side_effect=['[{"unit":"a.service"}]', "[]"] + ): + self.assertEqual(panel.failed_units(), "FAIL 1") + + def test_failed_query_does_not_report_healthy(self): + with patch.object(panel, "output", side_effect=RuntimeError("offline")): + status = panel.collect() + self.assertEqual(status["failed"], "FAIL ?") + self.assertIn("offline", status["errors"]) + + def test_updates_ignore_header(self): + with patch.object( + panel, + "output", + return_value="Listing...\na/stable 1 amd64 [upgradable from: 0]\n", + ): + self.assertEqual(panel.apt_updates(), "APT 1") + + def test_displays_do_not_depend_on_personal_connector_names(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + for name, value in [ + ("card1-DP-9", "connected"), + ("card0-eDP-1", "connected"), + ("card1-HDMI-A-1", "disconnected"), + ]: + (root / name).mkdir() + (root / name / "status").write_text(value) + self.assertEqual(panel.displays(root), "EXT 1") + + def test_update_action_quotes_source_directory(self): + with patch.object(panel, "output", return_value="/tmp/work tree'quoted\n"): + command = panel.action_command("update") + self.assertEqual(command[:2], ["/snap/bin/ghostty", "-e"]) + self.assertIn("just update", command[-1]) + self.assertIn("'\"'\"'", command[-1]) + + def test_extension_metadata_targets_corporate_gnome(self): + metadata = json.loads( + ( + ROOT + / "dot_local/share/gnome-shell/extensions/corporate-panel@dotfiles/metadata.json" + ).read_text() + ) + self.assertEqual(metadata["uuid"], "corporate-panel@dotfiles") + self.assertIn("50", metadata["shell-version"]) + + +if __name__ == "__main__": + unittest.main() |
