feat(template links): Sketch-style visual parameter builder

- link_form: template builder under the URL field — preset param chips
  (q/id/page/category/keyword/lang, CN labels) click to insert ?q={q} with
  smart ?/& separators; auto-parsed parameter rows (name + default input +
  remove); custom params (name + default -> append); live dark preview
  filling defaults (green) and flagging required (red); chips grey out
  when used; hidden on Custom/Action tabs; edits existing templates
- redirect_to_original: map GET query params (?q=weather) by name into
  template placeholders, keep path-param backward compat (go/alias/x
  fills every unfilled param), defaults otherwise; query wins over path
- tests: +5 template redirect cases (query map, multi-param, defaults,
  path compat, query-over-path)
This commit is contained in:
OpenClaw Sub-agent
2026-08-02 17:28:32 +10:00
parent 131154e183
commit 54a416c63c
3 changed files with 369 additions and 1 deletions
+52
View File
@@ -0,0 +1,52 @@
"""Tests for template-link redirects: query-param mapping, path-param
compat, and default-value fallback (the Sketch-style template builder
produces ?q={q,default=…} style URLs, so the redirect must honor GET)."""
import pytest
from django.test import Client
from django.urls import reverse
from links.models import Link
@pytest.fixture
def tpl_link(db):
return Link.objects.create(
alias="tpltest",
original_url="https://www.google.com/search?q={q,default=hello}&page={page,default=1}",
link_type=Link.LinkType.LINK,
)
@pytest.mark.django_db
def test_template_query_param_mapping(client, tpl_link):
resp = client.get(reverse("redirect_to_original", args=["tpltest"]), {"q": "weather"})
assert resp.status_code == 302
assert resp["Location"] == "https://www.google.com/search?q=weather&page=1"
@pytest.mark.django_db
def test_template_multiple_query_params(client, tpl_link):
resp = client.get(reverse("redirect_to_original", args=["tpltest"]), {"q": "stocks", "page": "3"})
assert resp.status_code == 302
assert resp["Location"] == "https://www.google.com/search?q=stocks&page=3"
@pytest.mark.django_db
def test_template_defaults_when_no_params(client, tpl_link):
resp = client.get(reverse("redirect_to_original", args=["tpltest"]))
assert resp.status_code == 302
assert resp["Location"] == "https://www.google.com/search?q=hello&page=1"
@pytest.mark.django_db
def test_template_path_param_backward_compat(client, tpl_link):
"""Old style go/alias/foo fills every unfilled param with foo."""
resp = client.get(reverse("redirect_to_original_with_param", args=["tpltest", "weather"]))
assert resp.status_code == 302
assert resp["Location"] == "https://www.google.com/search?q=weather&page=weather"
@pytest.mark.django_db
def test_template_path_plus_query_query_wins(client, tpl_link):
resp = client.get(reverse("redirect_to_original_with_param", args=["tpltest", "foo"]), {"q": "weather"})
assert resp.status_code == 302
assert resp["Location"] == "https://www.google.com/search?q=weather&page=foo"