Dev #5
+14
-1
@@ -605,7 +605,20 @@
|
||||
const found = [];
|
||||
root.querySelectorAll('[data-element-id]').forEach(function(node) {
|
||||
const sib = node.nextSibling;
|
||||
if (sib && sib.nodeType === Node.COMMENT_NODE && COMMENT_MARKERS.indexOf(sib.data) !== -1) {
|
||||
const hasCommentMarker = !!(sib && sib.nodeType === Node.COMMENT_NODE && COMMENT_MARKERS.indexOf(sib.data) !== -1);
|
||||
// Un élément actuellement CACHÉ par une condition de
|
||||
// visibilité (voir render_element_html.py) n'a pas de
|
||||
// commentaire marqueur à sa suite — sa balise porte
|
||||
// directement la classe "visibilityGated" à la place. Sans
|
||||
// ce second cas, un élément qui passe de VISIBLE à CACHÉ
|
||||
// n'était jamais retrouvé ici (seul le sens caché->visible
|
||||
// l'était, via le commentaire) : son ancien nœud visible
|
||||
// restait donc affiché indéfiniment à côté du nouveau,
|
||||
// maintenant visible, qui le remplace normalement (ex. une
|
||||
// icône "enveloppe fermée" jamais masquée en même temps que
|
||||
// "enveloppe ouverte" qui apparaît après avoir cliqué).
|
||||
const isHiddenPlaceholder = node.classList.contains('visibilityGated');
|
||||
if (hasCommentMarker || isHiddenPlaceholder) {
|
||||
found.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
"""Régression : après une action "Modifier une donnée" qui fait BASCULER
|
||||
une condition de visibilité (ex. une icône "enveloppe fermée" visible
|
||||
quand is_opened=faux, une "enveloppe ouverte" visible quand is_opened=vrai),
|
||||
templates/play.html::refreshRuntimeData() ne retrouvait, dans le HTML
|
||||
fraîchement régénéré, que l'élément qui REDEVIENT visible (repéré via le
|
||||
commentaire "<!--visibilityGated-->" posé après son contenu réel par
|
||||
_mark(), voir render_element_html.py) — jamais celui qui REDEVIENT
|
||||
CACHÉ, dont le placeholder ("<div class=\"visibilityGated\" ...
|
||||
style=\"display:none;\"></div>") n'a pas de commentaire à sa suite, juste
|
||||
une classe CSS sur lui-même. Résultat : l'ancien élément visible restait
|
||||
affiché indéfiniment à côté du nouveau qui apparaît en plus (les deux
|
||||
enveloppes en même temps).
|
||||
|
||||
Ces tests-ci ne peuvent pas simuler le DOM d'un navigateur (refreshRuntimeData
|
||||
est du JS) — ils verrouillent seulement le CONTRAT côté serveur dont
|
||||
dépend le correctif JS (findCommentMarkedDescendants, voir play.html) :
|
||||
un élément CACHÉ par condition de visibilité doit porter la classe
|
||||
"visibilityGated" directement sur sa balise, un élément VISIBLE sous
|
||||
condition doit porter le commentaire juste après. Voir aussi
|
||||
tests/test_data_binding_refresh_marker.py pour le marqueur "dataBound"."""
|
||||
import re
|
||||
|
||||
|
||||
def _create_screen(client, slug, name="Accueil"):
|
||||
resp = client.post(f"/game/{slug}/screens/new", data={"name": name}, follow_redirects=False)
|
||||
return int(re.search(r"/screens/(\d+)/edit", resp.headers["Location"]).group(1))
|
||||
|
||||
|
||||
def _add_icon_with_visibility(client, slug, screen_id, def_id, field_name, expected):
|
||||
resp = client.post(f"/game/{slug}/screens/{screen_id}/elements/add", data={"widget": "icone"}, follow_redirects=False)
|
||||
el_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
|
||||
client.post(f"/game/{slug}/elements/{el_id}/save", data={
|
||||
"ctrl_visibilite_cond_mode": "objet",
|
||||
"ctrl_visibilite_cond_definition_id": str(def_id),
|
||||
"ctrl_visibilite_cond_champ": field_name,
|
||||
"ctrl_visibilite_cond_operateur": "egal",
|
||||
"ctrl_visibilite_cond_valeur": expected,
|
||||
})
|
||||
return el_id
|
||||
|
||||
|
||||
def test_hidden_element_carries_the_class_marker_not_a_comment(client, game):
|
||||
resp = client.post(f"/game/{game}/objects/new", data={
|
||||
"object_name": "Email", "field_name[]": ["is_opened"],
|
||||
"field_type[]": ["booleen"], "field_relation[]": [""], "field_required[]": ["0"],
|
||||
"field_min[]": [""], "field_max[]": [""],
|
||||
}, follow_redirects=False)
|
||||
def_id = int(resp.headers["Location"].rstrip("/").split("/")[-1])
|
||||
client.post(f"/game/{game}/objects/{def_id}/data/new", data={"is_opened": "0"})
|
||||
|
||||
screen_id = _create_screen(client, game)
|
||||
closed_id = _add_icon_with_visibility(client, game, screen_id, def_id, "is_opened", "Non")
|
||||
open_id = _add_icon_with_visibility(client, game, screen_id, def_id, "is_opened", "Oui")
|
||||
|
||||
payload = client.get(f"/game/{game}/runtime-payload").get_json()
|
||||
elements = {e["id"]: e for e in payload["screens"][0]["elements"]}
|
||||
|
||||
closed_html = elements[closed_id]["rendered_html"]
|
||||
open_html = elements[open_id]["rendered_html"]
|
||||
|
||||
# is_opened=faux : l'enveloppe "fermée" est visible (marqueur en
|
||||
# commentaire, après son vrai contenu) ; l'enveloppe "ouverte" est
|
||||
# cachée (marqueur en CLASSE, directement sur le placeholder).
|
||||
assert "<!--visibilityGated-->" in closed_html
|
||||
assert f'data-element-id="{closed_id}"' in closed_html
|
||||
assert "display:none" not in closed_html
|
||||
|
||||
assert 'class="visibilityGated"' in open_html
|
||||
assert "display:none" in open_html
|
||||
assert f'data-element-id="{open_id}"' in open_html
|
||||
assert "<!--visibilityGated-->" not in open_html
|
||||
|
||||
|
||||
def test_toggling_the_field_swaps_which_element_carries_which_marker(client, game):
|
||||
resp = client.post(f"/game/{game}/objects/new", data={
|
||||
"object_name": "Email2", "field_name[]": ["is_opened"],
|
||||
"field_type[]": ["booleen"], "field_relation[]": [""], "field_required[]": ["0"],
|
||||
"field_min[]": [""], "field_max[]": [""],
|
||||
}, follow_redirects=False)
|
||||
def_id = int(resp.headers["Location"].rstrip("/").split("/")[-1])
|
||||
resp = client.post(f"/game/{game}/objects/{def_id}/data/new", data={"is_opened": "0"}, follow_redirects=False)
|
||||
|
||||
screen_id = _create_screen(client, game)
|
||||
closed_id = _add_icon_with_visibility(client, game, screen_id, def_id, "is_opened", "Non")
|
||||
open_id = _add_icon_with_visibility(client, game, screen_id, def_id, "is_opened", "Oui")
|
||||
|
||||
import db
|
||||
rows = db.list_rows(game, db.get_definition(game, def_id))
|
||||
row_id = rows[0]["id"]
|
||||
db.update_row(game, db.get_definition(game, def_id), row_id, {"is_opened": "on"})
|
||||
|
||||
payload = client.get(f"/game/{game}/runtime-payload").get_json()
|
||||
elements = {e["id"]: e for e in payload["screens"][0]["elements"]}
|
||||
|
||||
# is_opened=vrai maintenant : les rôles s'inversent complètement, aucun
|
||||
# des deux ne doit garder son ancien marqueur.
|
||||
assert 'class="visibilityGated"' in elements[closed_id]["rendered_html"]
|
||||
assert "<!--visibilityGated-->" in elements[open_id]["rendered_html"]
|
||||
Reference in New Issue
Block a user