Corrige la mauvaise interprétation de la demande initiale : la page dédiée "🗺️ Quêtes" (nav du jeu) est retirée au profit d'un nouvel onglet "🗺️ Quêtes" DANS l'éditeur de scène 2D, juste à côté de "🧩 Collision" — même liste tabulaire que l'onglet "📣 Événements" (titre/objectif/récompense/statut/résultat par ligne, pas des cartes), un clic sur une ligne ouvrant la modale d'arbre de dialogue déjà construite. L'assistant "+ Action" de l'éditeur de collision ouvre toujours cette même modale pour "Déclencher une quête". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
165 lines
7.1 KiB
Python
165 lines
7.1 KiB
Python
"""Éditeur de quêtes (voir db/quests/, routes/quests/, onglet "🗺️ Quêtes"
|
|
de l'éditeur de scène 2D, templates/scene_edit.html) : id, titre, objectif, récompense
|
|
(score), statut (nouvelle/en_cours/terminee), résultat (reussi/echoue),
|
|
et un dialogue par statut (3 colonnes — voir static/js/quests/
|
|
quest-editor.js et sanitize_quest_dialogues). Une quête vit pour TOUT le
|
|
jeu, jamais par écran (mêmes conventions que les événements
|
|
personnalisés, db/custom_events/)."""
|
|
import db
|
|
|
|
|
|
def _make_game(tmp_game_slug_cleanup, name="pytest_quests"):
|
|
return tmp_game_slug_cleanup(db.create_game(name))
|
|
|
|
|
|
def test_create_quest_has_sensible_defaults(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
assert quest["title"] == "Trouver la clé"
|
|
assert quest["objectif"] == ""
|
|
assert quest["recompense_score"] == 0
|
|
assert quest["status"] == "nouvelle"
|
|
assert quest["resultat"] is None
|
|
assert quest["dialogues"] == {}
|
|
|
|
|
|
def test_create_quest_falls_back_to_default_title_when_blank(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, " ")
|
|
assert quest["title"] == "Nouvelle quête"
|
|
|
|
|
|
def test_list_quests_orders_most_recent_first(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
first = db.create_quest(slug, "Première")
|
|
second = db.create_quest(slug, "Seconde")
|
|
ids = [q["id"] for q in db.list_quests(slug)]
|
|
assert ids[:2] == [second["id"], first["id"]]
|
|
|
|
|
|
def test_update_quest_only_applies_known_valid_fields(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
updated = db.update_quest(slug, quest["id"], {
|
|
"title": "Trouver la clé dorée", "objectif": "Fouiller le donjon",
|
|
"recompense_score": 50, "status": "en_cours", "resultat": "reussi",
|
|
"unknown_field": "ignoré", "id": 999999,
|
|
})
|
|
assert updated["title"] == "Trouver la clé dorée"
|
|
assert updated["objectif"] == "Fouiller le donjon"
|
|
assert updated["recompense_score"] == 50
|
|
assert updated["status"] == "en_cours"
|
|
assert updated["resultat"] == "reussi"
|
|
assert updated["id"] == quest["id"] # "id" dans fields est ignoré, jamais modifiable
|
|
|
|
|
|
def test_update_quest_rejects_invalid_status_and_blank_title(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
updated = db.update_quest(slug, quest["id"], {"status": "en_pause", "title": " "})
|
|
assert updated["status"] == "nouvelle" # inchangé, valeur invalide ignorée
|
|
assert updated["title"] == "Trouver la clé" # inchangé, titre vide ignoré
|
|
|
|
|
|
def test_update_quest_returns_none_for_missing_quest(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
assert db.update_quest(slug, 999999, {"title": "x"}) is None
|
|
|
|
|
|
def test_delete_quest_removes_it(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
db.delete_quest(slug, quest["id"])
|
|
assert db.get_quest(slug, quest["id"]) is None
|
|
|
|
|
|
def test_sanitize_quest_dialogues_keeps_only_valid_lines_per_status():
|
|
raw = {
|
|
"nouvelle": [{"speaker": "pnj", "text": "Bonjour !"}, {"speaker": "joueur", "text": " "}],
|
|
"en_cours": [{"speaker": "sorcier", "text": "invalide"}, "pas un objet"],
|
|
"terminee": "pas une liste",
|
|
"statut_inconnu": [{"speaker": "pnj", "text": "ignoré"}],
|
|
}
|
|
cleaned = db.sanitize_quest_dialogues(raw)
|
|
assert cleaned == {
|
|
"nouvelle": [{"speaker": "pnj", "text": "Bonjour !"}],
|
|
"en_cours": [],
|
|
"terminee": [],
|
|
}
|
|
|
|
|
|
def test_set_quest_dialogues_persists_all_three_columns(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
dialogues = {
|
|
"nouvelle": [{"speaker": "pnj", "text": "Peux-tu m'aider ?"}, {"speaker": "joueur", "text": "Bien sûr !"}],
|
|
"en_cours": [{"speaker": "pnj", "text": "Alors, cette clé ?"}],
|
|
"terminee": [{"speaker": "pnj", "text": "Merci beaucoup !"}],
|
|
}
|
|
db.set_quest_dialogues(slug, quest["id"], dialogues)
|
|
reloaded = db.get_quest(slug, quest["id"])
|
|
assert reloaded["dialogues"] == dialogues
|
|
|
|
|
|
def test_set_quest_dialogues_returns_none_for_missing_quest(tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
assert db.set_quest_dialogues(slug, 999999, {}) is None
|
|
|
|
|
|
# ---------- Routes ----------
|
|
|
|
def test_quest_new_route_creates_and_returns_json(client, tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
resp = client.post(f"/game/{slug}/quests/new", data='{"title": "Nouvelle quête du donjon"}', content_type="application/json")
|
|
assert resp.status_code == 200
|
|
quest = resp.get_json()["quest"]
|
|
assert quest["title"] == "Nouvelle quête du donjon"
|
|
assert db.get_quest(slug, quest["id"]) is not None
|
|
|
|
|
|
def test_quests_list_json_route_returns_all_quests(client, tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
db.create_quest(slug, "A")
|
|
db.create_quest(slug, "B")
|
|
resp = client.get(f"/game/{slug}/quests/list.json")
|
|
titles = {q["title"] for q in resp.get_json()["quests"]}
|
|
assert titles == {"A", "B"}
|
|
|
|
|
|
def test_quest_update_route_persists_fields(client, tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
resp = client.post(f"/game/{slug}/quests/{quest['id']}", data='{"recompense_score": 100, "status": "terminee"}', content_type="application/json")
|
|
assert resp.get_json()["quest"]["recompense_score"] == 100
|
|
assert resp.get_json()["quest"]["status"] == "terminee"
|
|
|
|
|
|
def test_quest_update_route_404s_for_missing_quest(client, game):
|
|
resp = client.post(f"/game/{game}/quests/999999", data="{}", content_type="application/json")
|
|
assert resp.status_code == 404
|
|
|
|
|
|
def test_quest_delete_route_removes_it(client, tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
resp = client.post(f"/game/{slug}/quests/{quest['id']}/delete")
|
|
assert resp.get_json() == {"ok": True}
|
|
assert db.get_quest(slug, quest["id"]) is None
|
|
|
|
|
|
def test_quest_dialogues_route_sanitizes_and_persists(client, tmp_game_slug_cleanup):
|
|
slug = _make_game(tmp_game_slug_cleanup)
|
|
quest = db.create_quest(slug, "Trouver la clé")
|
|
resp = client.post(
|
|
f"/game/{slug}/quests/{quest['id']}/dialogues",
|
|
data='{"dialogues": {"nouvelle": [{"speaker": "pnj", "text": "Salut !"}], "en_cours": [], "terminee": []}}',
|
|
content_type="application/json",
|
|
)
|
|
assert resp.get_json()["dialogues"]["nouvelle"] == [{"speaker": "pnj", "text": "Salut !"}]
|
|
assert db.get_quest(slug, quest["id"])["dialogues"]["nouvelle"] == [{"speaker": "pnj", "text": "Salut !"}]
|
|
|
|
|
|
def test_quest_dialogues_route_404s_for_missing_quest(client, game):
|
|
resp = client.post(f"/game/{game}/quests/999999/dialogues", data='{"dialogues": {}}', content_type="application/json")
|
|
assert resp.status_code == 404
|