feat: réactions dans le tiroir Comments (+ suppression) et refonte du menu de sélection (v7.67.0)
FlowDeck CI / lint (push) Canceled after 0s
FlowDeck CI / test (push) Canceled after 0s
FlowDeck CI / docker (push) Canceled after 0s

Tiroir Comments
- Section « Reactions » au-dessus des fils : émoji, compte (si >1) et texte
  ancré lu dans le DOM (reactionList() / _anchorText(), offsets source hors
  pastilles) ; l'état vide ne s'affiche plus s'il reste des réactions.
- Suppression : ✕ sur la ligne du tiroir (mes réactions) ET croix ::after au
  survol de la pastille sur l'émoji que j'ai posé (aucun nœud ajouté → offsets
  intacts). Deux entrées → removeReaction() → POST toggle → loadReactions().

Menu de sélection (maquette ASCII)
- Ligne 4 = rangée scannée : 💬 Comment (libellé) à gauche, 🙂+ réaction à
  droite (.ft-row-split) ; la réaction sort de la ligne 3.
- Entête Skills avec ≡ à droite (::after), liste Skills scrollable.
- ✨ Ask AI retiré de la toolbar : doublon exact de « Edit with AI » du pied.
- Écart volontaire à la maquette : le barré (T) de la ligne 2 est conservé.

Fix racine (flake de gate)
- _is_public_host attrape OSError (le garde SSRF renvoie un booléen, il ne
  lève plus) : un socket.timeout DNS ne bascule plus fetch_og_metadata dans
  son except Exception → repli silencieux → AssertionError.
- fixture stub_dns sur TestOGParser (même motif que test_agent_web_tools) :
  zéro vrai DNS dans les tests OG.

Tests: pytest tests/ -q -n auto → 1390 passed / 0 failed (flake corrigé) ;
ruff check app tests → OK ; e2e v764 (réactions dans le tiroir + suppression
A/B), v762 (nouvelle ligne 4), v766, v7641, v763 → 6 passed sur :8081
(image flowdeck:v7.67.0 rebuild + redéployée).
This commit is contained in:
2026-10-08 14:25:48 -04:00
parent db6bf03a00
commit d58c321c07
16 changed files with 345 additions and 17 deletions
+22
View File
@@ -10,8 +10,10 @@ Covers the five roadmap points:
from __future__ import annotations
import asyncio
import ipaddress
import json
import secrets
import socket
from pathlib import Path
import httpx
@@ -209,7 +211,27 @@ _OG_HTML = """<!doctype html><html><head>
</head><body>hi</body></html>"""
#: IP publique factice — TestOGParser ne doit dépendre ni du réseau HTTP
#: (MockTransport) ni du DNS : `_is_public_host` résout l'hôte pour décider si
#: l'IP est publique, et cette résolution échoue sous `pytest -n auto` (même
#: piège que test_agent_web_tools.stub_dns — d'où un flake récurrent ici).
PUBLIC_IP = "93.184.216.34"
class TestOGParser:
@pytest.fixture(autouse=True)
def stub_dns(self, monkeypatch):
real = socket.getaddrinfo
def fake_getaddrinfo(host, *args, **kwargs):
try:
ipaddress.ip_address(host)
except ValueError:
return [(socket.AF_INET, socket.SOCK_STREAM, 6, "", (PUBLIC_IP, 0))]
return real(host, *args, **kwargs)
monkeypatch.setattr(socket, "getaddrinfo", fake_getaddrinfo)
def test_parse_og_handles_attribute_order(self):
from app.services.og_fetcher import parse_og