feat(blueprint): mount srccfg editor on the config textarea
The textarea is preserved as the form field; the editor renders a contenteditable sibling and mirrors content back on every input. Form POST contract is untouched (covered by new round-trip test).
This commit is contained in:
parent
b203a83f58
commit
607970eb43
2 changed files with 53 additions and 1 deletions
|
|
@ -49,7 +49,7 @@
|
|||
<pre class="config-preview" aria-label="Auto-loaded overlay configs">{% for o in exposed %}exec {{ o.name }}.cfg
|
||||
{% endfor %}</pre>
|
||||
{% endif %}
|
||||
<textarea name="config" rows="8" spellcheck="false">{{ config_lines | join('\n') }}</textarea>
|
||||
<textarea name="config" rows="8" spellcheck="false" data-editor-language="srccfg">{{ config_lines | join('\n') }}</textarea>
|
||||
</div>
|
||||
</label>
|
||||
<button type="submit">Save blueprint</button>
|
||||
|
|
@ -92,4 +92,5 @@
|
|||
</div>
|
||||
</dialog>
|
||||
<script src="{{ url_for('static', filename='js/blueprint-overlay-picker.js') }}" defer></script>
|
||||
{% include '_editor_assets.html' %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
Loading…
Reference in a new issue