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).
45 lines
2.2 KiB
Python
45 lines
2.2 KiB
Python
"""7.67.0 — réactions emoji visibles dans le tiroir Comments + suppression."""
|
||
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
SRC = (ROOT / "static" / "js" / "page_editor_scripts.js").read_text(encoding="utf-8")
|
||
TPL = (ROOT / "app" / "templates" / "_page_editor_content.html").read_text(encoding="utf-8")
|
||
CSS = (ROOT / "static" / "css" / "app.css").read_text(encoding="utf-8")
|
||
|
||
|
||
def test_drawer_lists_reactions():
|
||
# section « Reactions » du tiroir : une ligne par plage × émoji
|
||
assert 'class="ct-reactions"' in TPL
|
||
assert 'x-for="r in reactionList()"' in TPL
|
||
assert '<span class="ct-re-emoji" x-text="r.emoji"></span>' in TPL
|
||
assert '<span class="ct-re-text" x-text="r.text"></span>' in TPL
|
||
# l'état vide ne s'affiche plus s'il reste des réactions
|
||
assert "comments.length === 0 && !reactionList().length" in TPL
|
||
# méthode : groupes déjà prêts côté API, texte source lu dans le DOM
|
||
assert "reactionList(){" in SRC
|
||
assert "_anchorText(bid,start,end)" in SRC
|
||
# le texte source ignore les pastilles (offsets identiques à sync())
|
||
assert "closest('.fd-react-chip'))continue;" in SRC
|
||
|
||
|
||
def test_reaction_removable_from_the_drawer():
|
||
assert 'title="Remove reaction"' in TPL
|
||
# le ✕ n'apparaît que sur MA réaction (les autres comptes sont en lecture)
|
||
assert 'x-show="r.mine" @click="removeReaction(r)"' in TPL
|
||
# suppression = POST toggle (le backend DELETE si ma ligne existe déjà)
|
||
assert "removeReaction(r){if(!r)return;this.toggleReaction(r.bid,r.start,r.end,r.emoji);}" in SRC
|
||
for sel in (".ct-section-title {", ".ct-reaction {", ".ct-re-emoji {", ".ct-re-text {"):
|
||
assert sel in CSS, sel
|
||
|
||
|
||
def test_reaction_removable_from_the_chip():
|
||
# chaque émoji de la pastille porte son data-emoji, `is-mine` = j'ai réagi
|
||
assert 'data-emoji="' in SRC
|
||
assert "parseInt(i.mine,10)?' is-mine':''" in SRC
|
||
assert ("d.removeReaction({bid:bid,start:start,end:end,"
|
||
"emoji:emo.getAttribute('data-emoji')})") in SRC
|
||
# la croix est un ::after (aucun nœud ajouté → offsets source inchangés)
|
||
assert ".fd-react-emo.is-mine::after {" in CSS
|
||
assert ".fd-react-chip:hover .fd-react-emo.is-mine::after { display: flex; }" in CSS
|