Fix Jauge always reading the object's most recent row

A Jauge could only target a whole object, not a specific record —
three gauges pointing at the same "value" field (e.g. Réputation/
Trésorerie/Confiance in one "jauge" object) all silently showed the
most recent row's value, with no way to tell them apart.

Add "Enregistrement (ligne) à suivre" (row_id) so a Jauge targets one
specific row, and "Champ contenant le nom" (champ_nom) to show a
label above the bar — both as dropdowns populated from the object's
actual fields/rows (previously "Champ numérique à afficher" was free
text the user had to type correctly by hand). No regression: without
row_id the widget still falls back to the latest row, exactly as
before.

Extract data_definition_options() (rows+fields for a definition) out
of routes/screens/screen_edit.py so controls_with_values.py can reuse
it server-side for the initial render; a small client-side handler
(bindJaugeDefinitionSelect) repopulates the same selects live when
the tracked object is changed without leaving the panel.

Verified live with Playwright: picking "jauge" then "Confiance" then
"value"/"name" in the panel renders an 80%-filled, green-leaning bar
labelled "Confiance" on /play — not the 20%/50% of the other rows.
This commit is contained in:
william
2026-08-23 18:02:02 +02:00
parent 4eac20a464
commit c9d3069f47
8 changed files with 204 additions and 30 deletions
+82
View File
@@ -1,6 +1,8 @@
"""Tests du widget Jauge (2.1 : binding live d'une donnée vers un style)."""
import re
import db
def _create_screen(client, slug, name="Accueil"):
resp = client.post(f"/game/{slug}/screens/new", data={"name": name}, follow_redirects=False)
@@ -83,6 +85,86 @@ def test_gauge_value_clamped_visually_when_out_of_range(client, game):
assert "width:100.0%" in html
# ---------- Choix de l'enregistrement (plusieurs jauges dans un même objet)
# et champ affiché comme nom au-dessus de la barre ----------
def _create_jauge_object_with_rows(client, slug):
"""Un seul objet "jauge" avec trois enregistrements (name/value), comme
dans le scénario remonté : Réputation/Trésorerie/Confiance, toutes à 50
— le bug était de ne pas pouvoir distinguer laquelle une Jauge suit."""
resp = client.post(f"/game/{slug}/objects/new", data={
"object_name": "jauge",
"field_name[]": ["name", "value"], "field_type[]": ["texte", "nombre_entier"],
"field_relation[]": ["", ""], "field_required[]": ["0", "0"],
}, follow_redirects=False)
def_id = int(resp.headers["Location"].rstrip("/").split("/")[-1])
for name, value in [("Réputation", "50"), ("Trésorerie", "20"), ("Confiance", "80")]:
client.post(f"/game/{slug}/objects/{def_id}/data/new", data={"name": name, "value": value}, follow_redirects=False)
# La route de création redirige vers la liste (pas d'id de ligne dans le
# Location) — on relit les lignes enregistrées pour retrouver leurs id.
definition = db.get_definition(slug, def_id)
row_ids = {r["name"]: r["id"] for r in db.list_rows(slug, definition)}
return def_id, row_ids
def test_gauge_row_id_distinguishes_between_records_of_the_same_object(client, game):
"""Le bug remonté : trois Jauges reliées au même objet (via "value")
affichaient toutes la même valeur (celle de la ligne la plus récente),
faute de pouvoir choisir LAQUELLE suivre. row_id doit permettre de
cibler "Confiance" (80%) plutôt que "Trésorerie" (20%)."""
def_id, row_ids = _create_jauge_object_with_rows(client, game)
screen_id = _create_screen(client, game)
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "jauge"}, follow_redirects=False)
jauge_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
client.post(f"/game/{game}/elements/{jauge_id}/save", data={
"ctrl_definition_id": str(def_id), "ctrl_row_id": str(row_ids["Confiance"]),
"ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
})
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
assert "width:80.0%" in html
def test_gauge_without_row_id_falls_back_to_latest_row_no_regression(client, game):
"""Sans row_id réglé (jauges créées avant ce champ, ou objet à une seule
ligne comme dans test_jauge.py historique) : comportement inchangé, la
ligne la plus récente."""
def_id, _row_ids = _create_jauge_object_with_rows(client, game)
screen_id = _create_screen(client, game)
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "jauge"}, follow_redirects=False)
jauge_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
client.post(f"/game/{game}/elements/{jauge_id}/save", data={
"ctrl_definition_id": str(def_id), "ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
})
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
assert "width:80.0%" in html # "Confiance", ajoutée en dernier
def test_gauge_name_field_renders_above_the_bar(client, game):
def_id, row_ids = _create_jauge_object_with_rows(client, game)
screen_id = _create_screen(client, game)
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "jauge"}, follow_redirects=False)
jauge_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
client.post(f"/game/{game}/elements/{jauge_id}/save", data={
"ctrl_definition_id": str(def_id), "ctrl_row_id": str(row_ids["Confiance"]),
"ctrl_champ": "value", "ctrl_champ_nom": "name", "ctrl_min": "0", "ctrl_max": "100",
})
html = client.get(f"/game/{game}/play").data.decode()
assert ">Confiance<" in html
def test_gauge_without_champ_nom_shows_no_name_no_regression(client, game):
def_id, row_ids = _create_jauge_object_with_rows(client, game)
screen_id = _create_screen(client, game)
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "jauge"}, follow_redirects=False)
jauge_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
client.post(f"/game/{game}/elements/{jauge_id}/save", data={
"ctrl_definition_id": str(def_id), "ctrl_row_id": str(row_ids["Confiance"]),
"ctrl_champ": "value", "ctrl_min": "0", "ctrl_max": "100",
})
html = client.get(f"/game/{game}/play").data.decode()
assert ">Confiance<" not in html
# ---------- Régression : clics multipliés après un rafraîchissement de
# données (bouton +10/-10 relié à une jauge) ----------