diff --git a/l4d2web/l4d2web/templates/blueprint_detail.html b/l4d2web/l4d2web/templates/blueprint_detail.html index ad0645c..5b67a06 100644 --- a/l4d2web/l4d2web/templates/blueprint_detail.html +++ b/l4d2web/l4d2web/templates/blueprint_detail.html @@ -49,7 +49,7 @@
{% for o in exposed %}exec {{ o.name }}.cfg
 {% endfor %}
{% endif %} - + @@ -92,4 +92,5 @@ +{% include '_editor_assets.html' %} {% endblock %} diff --git a/l4d2web/tests/test_blueprints.py b/l4d2web/tests/test_blueprints.py index 20982a3..103a608 100644 --- a/l4d2web/tests/test_blueprints.py +++ b/l4d2web/tests/test_blueprints.py @@ -253,6 +253,57 @@ def test_form_update_preserves_ordered_overlays_and_multiline_fields(user_client assert update.headers["Location"] == "/blueprints/1" +def test_blueprint_detail_renders_editor_assets(user_client) -> None: + with session_scope() as session: + user = session.scalar(select(User).where(User.username == "alice")) + blueprint = Blueprint(user_id=user.id, name="bp", arguments="[]", config="[]") + session.add(blueprint) + session.flush() + blueprint_id = blueprint.id + + response = user_client.get(f"/blueprints/{blueprint_id}") + assert response.status_code == 200 + body = response.get_data(as_text=True) + # Editor opts the textarea in via a data-attribute. + assert 'data-editor-language="srccfg"' in body + # All editor assets are referenced. + assert "static/vendor/prism.js" in body + assert "static/vendor/codejar.js" in body + assert "static/js/srccfg-grammar.js" in body + assert "static/js/editor.js" in body + assert "static/css/editor.css" in body + # Scripts are nonce'd (CSP regression guard). + assert 'nonce="' in body + + +def test_blueprint_config_form_post_still_round_trips(user_client) -> None: + # The editor is a visual layer; the form POST contract must be + # unaffected. This guards against accidentally renaming `config` or + # dropping it from form serialization. + with session_scope() as session: + user = session.scalar(select(User).where(User.username == "alice")) + blueprint = Blueprint(user_id=user.id, name="bp", arguments="[]", config="[]") + session.add(blueprint) + session.flush() + blueprint_id = blueprint.id + + update = user_client.post( + f"/blueprints/{blueprint_id}", + data={ + "name": "bp", + "arguments": "", + "config": "sv_cheats 1\nmp_gamemode coop", + "overlay_ids": [], + }, + headers={"X-CSRF-Token": "test-token"}, + ) + assert update.status_code == 302 + + with session_scope() as session: + bp = session.get(Blueprint, blueprint_id) + assert json.loads(bp.config) == ["sv_cheats 1", "mp_gamemode coop"] + + def test_blueprint_detail_renders_picker_list_and_select(user_client) -> None: with session_scope() as session: session.add(Overlay(name="o3", path="/opt/l4d2/overlays/o3"))