fix(editor): auto-default article metadata and de-duplicate SEO panels
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
from datetime import timedelta
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from django.test import override_settings
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.blog.models import ArticleIndexPage, ArticlePage
|
||||
from apps.blog.models import ArticleIndexPage, ArticlePage, ArticlePageAdminForm, Category
|
||||
from apps.blog.tests.factories import AuthorFactory
|
||||
|
||||
|
||||
@@ -273,3 +274,107 @@ def test_article_search_fields_include_summary():
|
||||
f.field_name for f in ArticlePage.search_fields if hasattr(f, "field_name")
|
||||
]
|
||||
assert "summary" in field_names
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_admin_form_relaxes_initial_required_fields(article_index, django_user_model):
|
||||
"""Slug/author/category/summary should not block initial draft validation."""
|
||||
user = django_user_model.objects.create_user(
|
||||
username="writer",
|
||||
email="writer@example.com",
|
||||
password="writer-pass",
|
||||
)
|
||||
form_class = ArticlePage.get_edit_handler().get_form_class()
|
||||
form = form_class(parent_page=article_index, for_user=user)
|
||||
|
||||
assert form.fields["slug"].required is False
|
||||
assert form.fields["author"].required is False
|
||||
assert form.fields["category"].required is False
|
||||
assert form.fields["summary"].required is False
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_admin_form_clean_applies_defaults(article_index, django_user_model, monkeypatch):
|
||||
"""Form clean should auto-fill slug/author/category/summary when omitted."""
|
||||
user = django_user_model.objects.create_user(
|
||||
username="writer",
|
||||
email="writer@example.com",
|
||||
password="writer-pass",
|
||||
first_name="Writer",
|
||||
last_name="User",
|
||||
)
|
||||
form_class = ArticlePage.get_edit_handler().get_form_class()
|
||||
form = form_class(parent_page=article_index, for_user=user)
|
||||
|
||||
body = [
|
||||
SimpleNamespace(block_type="code", value=SimpleNamespace(raw_code="print('ignore')")),
|
||||
SimpleNamespace(block_type="rich_text", value=SimpleNamespace(source="<p>Hello world body text.</p>")),
|
||||
]
|
||||
|
||||
def fake_super_clean(_self):
|
||||
_self.cleaned_data = {
|
||||
"title": "Auto Defaults Title",
|
||||
"slug": "",
|
||||
"author": None,
|
||||
"category": None,
|
||||
"summary": "",
|
||||
"body": body,
|
||||
}
|
||||
return _self.cleaned_data
|
||||
|
||||
mro = form.__class__.__mro__
|
||||
super_form_class = mro[mro.index(ArticlePageAdminForm) + 1]
|
||||
monkeypatch.setattr(super_form_class, "clean", fake_super_clean)
|
||||
cleaned = form.clean()
|
||||
|
||||
assert cleaned["slug"] == "auto-defaults-title"
|
||||
assert cleaned["author"] is not None
|
||||
assert cleaned["author"].user_id == user.id
|
||||
assert cleaned["category"] is not None
|
||||
assert cleaned["category"].slug == "general"
|
||||
assert cleaned["summary"] == "Hello world body text."
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_seo_tab_fields_not_duplicated():
|
||||
"""SEO tab should include each promote/SEO field only once."""
|
||||
handler = ArticlePage.get_edit_handler()
|
||||
seo_tab = next(panel for panel in handler.children if panel.heading == "SEO")
|
||||
|
||||
def flatten_field_names(panel):
|
||||
names = []
|
||||
for child in panel.children:
|
||||
if hasattr(child, "field_name"):
|
||||
names.append(child.field_name)
|
||||
else:
|
||||
names.extend(flatten_field_names(child))
|
||||
return names
|
||||
|
||||
field_names = flatten_field_names(seo_tab)
|
||||
assert field_names.count("slug") == 1
|
||||
assert field_names.count("seo_title") == 1
|
||||
assert field_names.count("search_description") == 1
|
||||
assert field_names.count("show_in_menus") == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_save_autogenerates_summary_when_missing(article_index):
|
||||
"""Model save fallback should generate summary from prose blocks."""
|
||||
category = Category.objects.create(name="Guides", slug="guides")
|
||||
author = AuthorFactory()
|
||||
article = ArticlePage(
|
||||
title="Summary Auto",
|
||||
slug="summary-auto",
|
||||
author=author,
|
||||
category=category,
|
||||
summary="",
|
||||
body=[
|
||||
("code", {"language": "python", "filename": "", "raw_code": "print('skip')"}),
|
||||
("rich_text", "<p>This should become the summary text.</p>"),
|
||||
],
|
||||
)
|
||||
|
||||
article_index.add_child(instance=article)
|
||||
article.save()
|
||||
|
||||
assert article.summary == "This should become the summary text."
|
||||
|
||||
Reference in New Issue
Block a user