150 lines
4.3 KiB
Python
150 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import types
|
|
from collections import deque
|
|
from typing import Any
|
|
|
|
from src.desktop_overlay import CompletionOverlayPayload, DesktopOverlayManager
|
|
|
|
|
|
class _FakeWidget:
|
|
def __init__(self, root: "_FakeTk", *, width: int = 360, height: int = 160) -> None:
|
|
self._root = root
|
|
self._width = width
|
|
self._height = height
|
|
self._exists = True
|
|
self._after_ids: dict[str, tuple[int, Any]] = {}
|
|
|
|
def withdraw(self) -> None:
|
|
return None
|
|
|
|
def overrideredirect(self, *_args: Any, **_kwargs: Any) -> None:
|
|
return None
|
|
|
|
def attributes(self, *_args: Any, **_kwargs: Any) -> None:
|
|
return None
|
|
|
|
def configure(self, *_args: Any, **_kwargs: Any) -> None:
|
|
return None
|
|
|
|
def pack(self, *_args: Any, **_kwargs: Any) -> None:
|
|
return None
|
|
|
|
def place(self, *_args: Any, **_kwargs: Any) -> None:
|
|
return None
|
|
|
|
def update_idletasks(self) -> None:
|
|
return None
|
|
|
|
def winfo_width(self) -> int:
|
|
return self._width
|
|
|
|
def winfo_height(self) -> int:
|
|
return self._height
|
|
|
|
def winfo_exists(self) -> bool:
|
|
return self._exists
|
|
|
|
def geometry(self, *_args: Any, **_kwargs: Any) -> None:
|
|
return None
|
|
|
|
def deiconify(self) -> None:
|
|
return None
|
|
|
|
def destroy(self) -> None:
|
|
self._exists = False
|
|
|
|
def after(self, delay_ms: int, callback: Any) -> str:
|
|
after_id = self._root._schedule(delay_ms, callback)
|
|
self._after_ids[after_id] = (delay_ms, callback)
|
|
return after_id
|
|
|
|
def after_cancel(self, after_id: str) -> None:
|
|
self._after_ids.pop(after_id, None)
|
|
self._root._cancel(after_id)
|
|
|
|
|
|
class _FakeButton(_FakeWidget):
|
|
def __init__(self, root: "_FakeTk", command: Any | None = None, **_kwargs: Any) -> None:
|
|
super().__init__(root)
|
|
self.command = command
|
|
|
|
|
|
class _FakeTk(_FakeWidget):
|
|
def __init__(self) -> None:
|
|
super().__init__(self)
|
|
self._events: deque[tuple[str, int, Any]] = deque()
|
|
self._event_seq = 0
|
|
self.scheduled_delays: list[int] = []
|
|
self.cards: list[_FakeWidget] = []
|
|
|
|
def withdraw(self) -> None:
|
|
return None
|
|
|
|
def winfo_screenwidth(self) -> int:
|
|
return 1920
|
|
|
|
def _schedule(self, delay_ms: int, callback: Any) -> str:
|
|
after_id = f"after-{self._event_seq}"
|
|
self._event_seq += 1
|
|
self.scheduled_delays.append(delay_ms)
|
|
self._events.append((after_id, delay_ms, callback))
|
|
return after_id
|
|
|
|
def _cancel(self, after_id: str) -> None:
|
|
self._events = deque(event for event in self._events if event[0] != after_id)
|
|
|
|
def mainloop(self) -> None:
|
|
iterations = 0
|
|
while self._events and iterations < 20:
|
|
after_id, _delay_ms, callback = self._events.popleft()
|
|
iterations += 1
|
|
callback()
|
|
if any(not card.winfo_exists() for card in self.cards):
|
|
return
|
|
|
|
|
|
class _FakeTkModule(types.SimpleNamespace):
|
|
def __init__(self, root: _FakeTk) -> None:
|
|
super().__init__()
|
|
self._root = root
|
|
|
|
def Tk(self) -> _FakeTk:
|
|
return self._root
|
|
|
|
def Toplevel(self, _root: _FakeTk) -> _FakeWidget:
|
|
card = _FakeWidget(self._root)
|
|
self._root.cards.append(card)
|
|
return card
|
|
|
|
def Frame(self, root: _FakeWidget, **_kwargs: Any) -> _FakeWidget:
|
|
return _FakeWidget(root._root)
|
|
|
|
def Label(self, root: _FakeWidget, **_kwargs: Any) -> _FakeWidget:
|
|
return _FakeWidget(root._root)
|
|
|
|
def Button(self, root: _FakeWidget, command: Any | None = None, **_kwargs: Any) -> _FakeButton:
|
|
return _FakeButton(root._root, command=command)
|
|
|
|
|
|
def test_completion_overlay_auto_dismisses(monkeypatch: Any) -> None:
|
|
root = _FakeTk()
|
|
fake_tk = _FakeTkModule(root)
|
|
monkeypatch.setitem(__import__("sys").modules, "tkinter", fake_tk)
|
|
|
|
manager = DesktopOverlayManager(auto_dismiss_seconds=0.01)
|
|
manager._queue.put(
|
|
CompletionOverlayPayload(
|
|
job_id="job-123",
|
|
objective="Write a report",
|
|
return_message="Finished",
|
|
steps=5,
|
|
elapsed_seconds=12.4,
|
|
)
|
|
)
|
|
|
|
manager._ui_main()
|
|
|
|
assert any(delay == 10 for delay in root.scheduled_delays)
|
|
assert root.cards[0]._exists is False
|