feat(gui): Add blinking APPROVAL PENDING badge to MMA dashboard
This commit is contained in:
13
gui_2.py
13
gui_2.py
@@ -2685,6 +2685,19 @@ class App:
|
|||||||
if self.active_tier:
|
if self.active_tier:
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
imgui.text_colored(C_VAL, f"| Active: {self.active_tier}")
|
imgui.text_colored(C_VAL, f"| Active: {self.active_tier}")
|
||||||
|
# Approval pending indicator
|
||||||
|
any_pending = (
|
||||||
|
self._pending_mma_spawn is not None or
|
||||||
|
self._pending_mma_approval is not None or
|
||||||
|
self._pending_ask_dialog
|
||||||
|
)
|
||||||
|
if any_pending:
|
||||||
|
alpha = abs(math.sin(time.time() * 5))
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.3, alpha), " APPROVAL PENDING")
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Go to Approval"):
|
||||||
|
pass # scroll/focus handled by existing dialog rendering
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
# 2. Active Track Info
|
# 2. Active Track Info
|
||||||
if self.active_track:
|
if self.active_track:
|
||||||
|
|||||||
34
scripts/tasks/gui_ux_1_2_impl.toml
Normal file
34
scripts/tasks/gui_ux_1_2_impl.toml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
role = "tier3-worker"
|
||||||
|
docs = ["conductor/workflow.md"]
|
||||||
|
prompt = """
|
||||||
|
Implement Task 1.2 in @gui_2.py: add a blinking "APPROVAL PENDING" badge in _render_mma_dashboard.
|
||||||
|
|
||||||
|
LOCATION: In _render_mma_dashboard, after the block that renders the Status line and active_tier text (around line 2686-2690 after this existing code):
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.text(f"Status: {self.mma_status.upper()}")
|
||||||
|
if self.active_tier:
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.text_colored(C_VAL, f"| Active: {self.active_tier}")
|
||||||
|
|
||||||
|
ADD immediately after (before the next imgui.separator()):
|
||||||
|
# Approval pending indicator
|
||||||
|
any_pending = (
|
||||||
|
self._pending_mma_spawn is not None or
|
||||||
|
self._pending_mma_approval is not None or
|
||||||
|
self._pending_ask_dialog
|
||||||
|
)
|
||||||
|
if any_pending:
|
||||||
|
alpha = abs(math.sin(time.time() * 5))
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.3, alpha), " APPROVAL PENDING")
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("Go to Approval"):
|
||||||
|
pass # scroll/focus handled by existing dialog rendering
|
||||||
|
|
||||||
|
Also ensure `import math` is present at the top of gui_2.py (check if it's already imported — if not, add it alongside the other stdlib imports).
|
||||||
|
|
||||||
|
TESTS that must pass: @tests/test_mma_approval_indicators.py
|
||||||
|
Also confirm @tests/test_mma_dashboard_streams.py still passes.
|
||||||
|
|
||||||
|
Use exactly 1-space indentation for Python code.
|
||||||
|
"""
|
||||||
44
scripts/tasks/gui_ux_1_2_tests.toml
Normal file
44
scripts/tasks/gui_ux_1_2_tests.toml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
role = "tier3-worker"
|
||||||
|
docs = ["conductor/workflow.md"]
|
||||||
|
prompt = """
|
||||||
|
Create tests/test_mma_approval_indicators.py — failing tests for Task 1.2 of comprehensive_gui_ux track.
|
||||||
|
|
||||||
|
CONTEXT: Task 1.2 adds a blinking "APPROVAL PENDING" badge after the Status line in _render_mma_dashboard in @gui_2.py. It checks self._pending_mma_spawn, self._pending_mma_approval, and self._pending_ask_dialog.
|
||||||
|
|
||||||
|
TESTS TO WRITE (all should FAIL against current code — test new behaviour):
|
||||||
|
|
||||||
|
Use the same _make_app / _make_imgui_mock pattern as @tests/test_mma_dashboard_streams.py but add these attributes:
|
||||||
|
app._pending_mma_spawn = None
|
||||||
|
app._pending_mma_approval = None
|
||||||
|
app._pending_ask_dialog = False
|
||||||
|
|
||||||
|
1. test_no_approval_badge_when_idle:
|
||||||
|
- All three pending attrs are None/False
|
||||||
|
- Patch gui_2.imgui, call App._render_mma_dashboard(app)
|
||||||
|
- Assert imgui.text_colored was NOT called with any string containing "APPROVAL PENDING"
|
||||||
|
|
||||||
|
2. test_approval_badge_shown_when_spawn_pending:
|
||||||
|
- app._pending_mma_spawn = {"ticket_id": "T-001"}
|
||||||
|
- Patch gui_2.imgui, patch 'gui_2.math' (so math.sin returns 0.8)
|
||||||
|
- Call App._render_mma_dashboard(app)
|
||||||
|
- Collect all text_colored call args as a single joined string
|
||||||
|
- Assert "APPROVAL PENDING" appears in that string
|
||||||
|
|
||||||
|
3. test_approval_badge_shown_when_mma_approval_pending:
|
||||||
|
- app._pending_mma_approval = {"step": "test"}
|
||||||
|
- Same patch pattern
|
||||||
|
- Assert "APPROVAL PENDING" in text_colored calls
|
||||||
|
|
||||||
|
4. test_approval_badge_shown_when_ask_dialog_pending:
|
||||||
|
- app._pending_ask_dialog = True
|
||||||
|
- Same patch pattern
|
||||||
|
- Assert "APPROVAL PENDING" in text_colored calls
|
||||||
|
|
||||||
|
IMPLEMENTATION DETAILS:
|
||||||
|
- Import: import pytest, math, from unittest.mock import patch, MagicMock, call
|
||||||
|
- Use the _make_app helper from test_mma_dashboard_streams (copy it or import it — prefer copying to keep tests self-contained)
|
||||||
|
- For _make_imgui_mock: same as test_mma_dashboard_streams, also set imgui_mock.ImVec4.return_value = MagicMock()
|
||||||
|
- Patch 'gui_2.math' where needed: with patch('gui_2.math') as mock_math: mock_math.sin.return_value = 0.8
|
||||||
|
- Use 1-space indentation throughout.
|
||||||
|
- All 4 tests should FAIL against current gui_2.py (which has no approval badge logic yet).
|
||||||
|
"""
|
||||||
102
tests/test_mma_approval_indicators.py
Normal file
102
tests/test_mma_approval_indicators.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
import math
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import patch, MagicMock, call
|
||||||
|
|
||||||
|
from gui_2 import App
|
||||||
|
|
||||||
|
|
||||||
|
def _make_app(**kwargs):
|
||||||
|
app = MagicMock(spec=App)
|
||||||
|
app.mma_streams = kwargs.get("mma_streams", {})
|
||||||
|
app.mma_tier_usage = kwargs.get("mma_tier_usage", {
|
||||||
|
"Tier 1": {"input": 0, "output": 0},
|
||||||
|
"Tier 2": {"input": 0, "output": 0},
|
||||||
|
"Tier 3": {"input": 0, "output": 0},
|
||||||
|
"Tier 4": {"input": 0, "output": 0},
|
||||||
|
})
|
||||||
|
app.tracks = kwargs.get("tracks", [])
|
||||||
|
app.active_track = kwargs.get("active_track", None)
|
||||||
|
app.active_tickets = kwargs.get("active_tickets", [])
|
||||||
|
app.mma_status = kwargs.get("mma_status", "idle")
|
||||||
|
app.active_tier = kwargs.get("active_tier", None)
|
||||||
|
app.mma_step_mode = kwargs.get("mma_step_mode", False)
|
||||||
|
app._pending_mma_spawn = kwargs.get("_pending_mma_spawn", None)
|
||||||
|
app._pending_mma_approval = kwargs.get("_pending_mma_approval", None)
|
||||||
|
app._pending_ask_dialog = kwargs.get("_pending_ask_dialog", False)
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
def _make_imgui_mock():
|
||||||
|
m = MagicMock()
|
||||||
|
m.begin_table.return_value = False
|
||||||
|
m.begin_child.return_value = False
|
||||||
|
m.checkbox.return_value = (False, False)
|
||||||
|
m.collapsing_header.return_value = False
|
||||||
|
m.ImVec2.return_value = MagicMock()
|
||||||
|
m.ImVec4.return_value = MagicMock()
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def _collect_text_colored_args(imgui_mock):
|
||||||
|
"""Return a single joined string of all text_colored second-arg strings."""
|
||||||
|
parts = []
|
||||||
|
for c in imgui_mock.text_colored.call_args_list:
|
||||||
|
args = c.args
|
||||||
|
if len(args) >= 2:
|
||||||
|
parts.append(str(args[1]))
|
||||||
|
return " ".join(parts)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMMAApprovalIndicators:
|
||||||
|
|
||||||
|
def test_no_approval_badge_when_idle(self):
|
||||||
|
"""No 'APPROVAL PENDING' badge when all pending attrs are None/False."""
|
||||||
|
app = _make_app(
|
||||||
|
_pending_mma_spawn=None,
|
||||||
|
_pending_mma_approval=None,
|
||||||
|
_pending_ask_dialog=False,
|
||||||
|
)
|
||||||
|
imgui_mock = _make_imgui_mock()
|
||||||
|
with patch("gui_2.imgui", imgui_mock):
|
||||||
|
App._render_mma_dashboard(app)
|
||||||
|
combined = _collect_text_colored_args(imgui_mock)
|
||||||
|
assert "APPROVAL PENDING" not in combined, (
|
||||||
|
"text_colored called with 'APPROVAL PENDING' when no approval is pending"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_approval_badge_shown_when_spawn_pending(self):
|
||||||
|
"""'APPROVAL PENDING' badge must appear when _pending_mma_spawn is set."""
|
||||||
|
app = _make_app(_pending_mma_spawn={"ticket_id": "T-001"})
|
||||||
|
imgui_mock = _make_imgui_mock()
|
||||||
|
with patch("gui_2.imgui", imgui_mock), patch("gui_2.math") as mock_math:
|
||||||
|
mock_math.sin.return_value = 0.8
|
||||||
|
App._render_mma_dashboard(app)
|
||||||
|
combined = _collect_text_colored_args(imgui_mock)
|
||||||
|
assert "APPROVAL PENDING" in combined, (
|
||||||
|
"text_colored not called with 'APPROVAL PENDING' when _pending_mma_spawn is set"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_approval_badge_shown_when_mma_approval_pending(self):
|
||||||
|
"""'APPROVAL PENDING' badge must appear when _pending_mma_approval is set."""
|
||||||
|
app = _make_app(_pending_mma_approval={"step": "test"})
|
||||||
|
imgui_mock = _make_imgui_mock()
|
||||||
|
with patch("gui_2.imgui", imgui_mock), patch("gui_2.math") as mock_math:
|
||||||
|
mock_math.sin.return_value = 0.8
|
||||||
|
App._render_mma_dashboard(app)
|
||||||
|
combined = _collect_text_colored_args(imgui_mock)
|
||||||
|
assert "APPROVAL PENDING" in combined, (
|
||||||
|
"text_colored not called with 'APPROVAL PENDING' when _pending_mma_approval is set"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_approval_badge_shown_when_ask_dialog_pending(self):
|
||||||
|
"""'APPROVAL PENDING' badge must appear when _pending_ask_dialog is True."""
|
||||||
|
app = _make_app(_pending_ask_dialog=True)
|
||||||
|
imgui_mock = _make_imgui_mock()
|
||||||
|
with patch("gui_2.imgui", imgui_mock), patch("gui_2.math") as mock_math:
|
||||||
|
mock_math.sin.return_value = 0.8
|
||||||
|
App._render_mma_dashboard(app)
|
||||||
|
combined = _collect_text_colored_args(imgui_mock)
|
||||||
|
assert "APPROVAL PENDING" in combined, (
|
||||||
|
"text_colored not called with 'APPROVAL PENDING' when _pending_ask_dialog is True"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user