dnscms: upgrade to wagtail 8 and django 6.1, test upgrade-sensitive admin surfaces
dnscms / ci (push) Successful in 59s

This commit is contained in:
2026-08-31 01:21:11 +02:00
parent 531fa77e81
commit ceddc7d099
5 changed files with 446 additions and 143 deletions
+75
View File
@@ -0,0 +1,75 @@
"""Admin view tests: the choose-parent redirect and a smoke test over the
admin URLs served by our viewsets."""
from urllib.parse import parse_qs, urlparse
import pytest
from django.urls import reverse
from tests.conftest import EventIndexFactory, GenericPageFactory
def _parsed(response):
url = urlparse(response["Location"])
return url.path, parse_qs(url.query)
def test_choose_parent_no_valid_parent_renders_form(admin_client, db):
response = admin_client.get(reverse("events:choose_parent"))
assert response.status_code == 200
def test_choose_parent_single_parent_redirects_with_next(admin_client, event_index):
response = admin_client.get(reverse("events:choose_parent"))
assert response.status_code == 302
path, query = _parsed(response)
assert path == reverse("wagtailadmin_pages:add", args=["events", "eventpage", event_index.pk])
assert query["next"] == [reverse("events:index")]
def test_choose_parent_multiple_parents_shows_form_then_redirects(admin_client, event_index):
# max_count=1 stops a second EventIndex being created through the admin,
# but the view logic only cares how many instances exist.
second_index = EventIndexFactory(parent=event_index.get_parent(), slug="events-2")
response = admin_client.get(reverse("events:choose_parent"))
assert response.status_code == 200
response = admin_client.post(reverse("events:choose_parent"), {"parent_page": second_index.pk})
assert response.status_code == 302
path, query = _parsed(response)
assert path == reverse("wagtailadmin_pages:add", args=["events", "eventpage", second_index.pk])
assert query["next"] == [reverse("events:index")]
@pytest.mark.parametrize(
"url_name",
[
"wagtailadmin_home",
"events:index",
"events:choose_parent",
"associations:index",
"news:index",
"venues:index",
"wagtailimages:index",
"wagtailsnippets_events_eventorganizer:list",
"association_chooser:choose",
"event_organizer_chooser:choose",
],
)
def test_admin_views_render(admin_client, db, url_name):
response = admin_client.get(reverse(url_name))
assert response.status_code == 200
def test_page_edit_view_renders_with_custom_editor_assets(admin_client, home_page):
"""The page editor must load with our Draftail feature and editor JS wired in."""
page = GenericPageFactory(parent=home_page, slug="edit-me")
response = admin_client.get(reverse("wagtailadmin_pages:edit", args=[page.pk]))
assert response.status_code == 200
content = response.content.decode()
assert "button-link" in content # dnscms.rich_text feature on the editor
assert "js/page-editor.js" in content # insert_editor_js hook output
+51
View File
@@ -0,0 +1,51 @@
"""Tests for the custom "button" rich text link feature (dnscms/rich_text.py).
- DB HTML -> front-end HTML (expand_db_html)
- DB HTML -> ContentState (editor load)
- ContentState -> DB HTML (editor save)
"""
import json
from wagtail.admin.rich_text.converters.contentstate import ContentstateConverter
from wagtail.rich_text import expand_db_html
DB_HTML = '<a linktype="button" data-href="/billetter/">Kjøp billett</a>'
def test_expand_db_html_rewrites_button_link():
assert expand_db_html(DB_HTML) == '<a class="button" href="/billetter/">Kjøp billett</a>'
def test_expand_db_html_escapes_href():
html = expand_db_html('<a linktype="button" data-href="/x/?a=1&amp;b=2">B</a>')
assert html == '<a class="button" href="/x/?a=1&amp;b=2">B</a>'
def test_expand_db_html_missing_href_falls_back_to_empty():
assert expand_db_html('<a linktype="button">B</a>') == '<a class="button" href="">B</a>'
def test_db_html_to_contentstate():
"""Editor load: the linktype anchor becomes a MUTABLE BUTTON entity."""
converter = ContentstateConverter(features=["button-link"])
contentstate = json.loads(converter.from_database_format(DB_HTML))
entities = list(contentstate["entityMap"].values())
assert len(entities) == 1
entity = entities[0]
assert entity["type"] == "BUTTON"
assert entity["mutability"] == "MUTABLE"
assert entity["data"] == {"url": "/billetter/"}
block = contentstate["blocks"][0]
assert block["text"] == "Kjøp billett"
assert len(block["entityRanges"]) == 1
def test_contentstate_roundtrip_preserves_button_link():
"""Editor load + save must not lose or mangle the stored linktype anchor."""
stored = f'<p data-block-key="abc12">{DB_HTML}</p>'
converter = ContentstateConverter(features=["button-link"])
roundtripped = converter.to_database_format(converter.from_database_format(stored))
assert roundtripped == stored
+51
View File
@@ -0,0 +1,51 @@
"""Tests for the admin-UI hooks in dnscms/wagtail_hooks.py.
Both hooks run against Wagtail admin internals (the page action menu, editor
JS injection) — the surface most likely to churn silently across Wagtail
upgrades.
"""
from dataclasses import dataclass
from dnscms.wagtail_hooks import editor_js, make_publish_default_action
@dataclass
class FakeMenuItem:
name: str
def _names(items):
return [item.name for item in items]
def test_make_publish_default_action_moves_publish_first():
menu_items = [
FakeMenuItem("action-save-draft"),
FakeMenuItem("action-submit"),
FakeMenuItem("action-publish"),
FakeMenuItem("action-unpublish"),
]
make_publish_default_action(menu_items, request=None, context={})
assert _names(menu_items) == [
"action-publish",
"action-save-draft",
"action-submit",
"action-unpublish",
]
def test_make_publish_default_action_without_publish_is_a_noop():
menu_items = [FakeMenuItem("action-save-draft"), FakeMenuItem("action-submit")]
make_publish_default_action(menu_items, request=None, context={})
assert _names(menu_items) == ["action-save-draft", "action-submit"]
def test_editor_js_returns_script_tag():
html = editor_js()
assert html.startswith("<script src=")
assert "js/page-editor.js" in html