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"))