Files
e5c52912dd Feature/remote desktop (#7)
* feat: add remote desktop streaming for Linux, Windows, and macOS

Introduce dashboard remote desktop with WebSocket JPEG streaming, host input injection, and admin control panel shortcuts. Add backend desktop domain modules, Linux install scripts, native capture on Win/Mac, session supersede, and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: stop misleading Mac remote-desktop setup prompts

Only surface Linux install/start commands on Linux, probe macOS TCC
for missing permissions, and hide setup banners once streaming.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: make desktop setup mypy-clean on Windows and Linux CI

Use posix_compat.geteuid instead of os.geteuid, and drop unused
type: ignore on optional macOS TCC imports.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: make desktop capture/input unit tests CI-safe

Force linux platform for virtual-display paths and avoid requiring
Pillow/pynput extras in the default test environment.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: polish remote desktop install/uninstall UX and bundle Linux scripts

Ship virtual-desktop scripts in the package, add localized SSE install/uninstall
flows with viewport progress, and fix uninstall success detection on hosts that
keep a usable display after the virtual stack is removed.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: harden desktop install SSE streaming and clear dashboard lint

Read apt/subprocess output in chunks to avoid asyncio readline crashes,
surface friendly apt-lock errors, and fix three dashboard eslint issues.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-12 22:35:24 +08:00

57 lines
1.9 KiB
Python

"""Tests for desktop input injection."""
from __future__ import annotations
from unittest.mock import MagicMock
from octop.infra.desktop.input import InputInjector
def _force_linux(monkeypatch) -> None:
# InputInjector decides xdotool via is_linux_virtual_display (capture.sys.platform).
monkeypatch.setattr("octop.infra.desktop.capture.sys.platform", "linux")
def test_virtual_display_uses_xdotool_for_click(monkeypatch) -> None:
_force_linux(monkeypatch)
calls: list[list[str]] = []
def fake_run(display: str | None, args: list[str]) -> bool:
calls.append(args)
return True
monkeypatch.setattr("octop.infra.desktop.input.shutil.which", lambda name: "/usr/bin/xdotool")
monkeypatch.setattr("octop.infra.desktop.input._run_xdotool", fake_run)
inj = InputInjector(display=":99")
inj.click(120, 340, button="left")
assert calls == [["mousemove", "120", "340", "click", "1"]]
def test_physical_display_uses_pynput_for_click(monkeypatch) -> None:
_force_linux(monkeypatch)
inj = InputInjector(display=":0")
mouse = MagicMock()
monkeypatch.setattr(inj, "_controllers", lambda: (mouse, MagicMock()))
monkeypatch.setattr(inj, "_with_display", lambda fn: fn())
monkeypatch.setattr(inj, "_button", lambda name: name)
inj.click(10, 20, button="right", clicks=2)
mouse.position = (10, 20)
mouse.click.assert_called_once_with("right", 2)
def test_virtual_display_does_not_init_pynput_on_click(monkeypatch) -> None:
_force_linux(monkeypatch)
monkeypatch.setattr("octop.infra.desktop.input.shutil.which", lambda name: "/usr/bin/xdotool")
monkeypatch.setattr("octop.infra.desktop.input._run_xdotool", lambda *_a, **_k: True)
inj = InputInjector(display=":99")
controllers = MagicMock(side_effect=AssertionError("pynput should not be used"))
monkeypatch.setattr(inj, "_controllers", controllers)
inj.click(1, 2)
controllers.assert_not_called()