"""Son — Phase 6 du plan d'extension du moteur : musique de fond par écran (en boucle, suit le cycle de vie de l'écran) et action ponctuelle "Jouer un son" (effet sonore, non bouclé). Comme le reste du graphe de logique côté client, la lecture audio RÉELLE n'est pas testable sans navigateur (voir play_js_bundle() — on vérifie que la logique existe dans le JS livré et que les données persistent, pas qu'elles jouent un son).""" import json import re from conftest import play_js_bundle, screen_edit_js_bundle import screens 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_flow_node(client, slug, screen_id, **fields): resp = client.post( f"/game/{slug}/screens/{screen_id}/flow/nodes/add", data=json.dumps(fields), content_type="application/json", ) assert resp.status_code == 200 return resp.get_json()["id"] def test_screen_background_music_persists(client, game): screen_id = _create_screen(client, game) resp = client.post( f"/game/{game}/screens/{screen_id}/background-music", data=json.dumps({"url": "https://example.com/song.mp3"}), content_type="application/json", ) assert resp.status_code == 200 screen = screens.get_screen(game, screen_id) assert screen["background_music_url"] == "https://example.com/song.mp3" def test_screen_background_music_empty_string_clears_it(client, game): screen_id = _create_screen(client, game) client.post( f"/game/{game}/screens/{screen_id}/background-music", data=json.dumps({"url": "https://example.com/song.mp3"}), content_type="application/json", ) client.post( f"/game/{game}/screens/{screen_id}/background-music", data=json.dumps({"url": ""}), content_type="application/json", ) screen = screens.get_screen(game, screen_id) assert screen["background_music_url"] is None def test_play_payload_exposes_background_music_per_screen(client, game): screen_id = _create_screen(client, game) client.post( f"/game/{game}/screens/{screen_id}/background-music", data=json.dumps({"url": "https://example.com/song.mp3"}), content_type="application/json", ) html = client.get(f"/game/{game}/runtime-payload").get_json() screen_payload = next(s for s in html["screens"] if s["id"] == screen_id) assert screen_payload["background_music_url"] == "https://example.com/song.mp3" def test_sound_action_node_persists_url(client, game): screen_id = _create_screen(client, game) node_id = _add_flow_node( client, game, screen_id, node_type="action", action_type="jouer_son", data_value="https://example.com/effect.wav", ) node = screens.get_flow_node(game, node_id) assert node["action_type"] == "jouer_son" assert node["data_value"] == "https://example.com/effect.wav" def test_play_page_exposes_sound_runtime(client, game): js = play_js_bundle() assert "runScreenBackgroundMusic" in js assert "'jouer_son'" in js assert "new Audio" in js def test_screen_edit_exposes_sound_editors(client, game): screen_id = _create_screen(client, game) html = client.get(f"/game/{game}/screens/{screen_id}/edit").get_data(as_text=True) assert "screen-bg-music-url" in html assert "nf-action-sound-fields" in html assert "nf-action-sound-url" in html js = screen_edit_js_bundle() assert "jouer_son" in js assert "bindScreenBackgroundMusicField" in js