Initial commit

This commit is contained in:
2024-08-27 20:33:44 +02:00
commit 1f1832267d
14794 changed files with 1599592 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
from django.forms import CheckboxInput, HiddenInput
from django.test import TestCase, override_settings
from wagtail.contrib.simple_translation.forms import SubmitTranslationForm
from wagtail.models import Locale, Page
from wagtail.test.i18n.models import TestPage
from wagtail.test.utils import WagtailTestUtils
@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
)
class TestSubmitPageTranslation(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")
self.en_homepage = Page.objects.get(depth=2)
self.fr_homepage = self.en_homepage.copy_for_translation(self.fr_locale)
self.de_homepage = self.en_homepage.copy_for_translation(self.de_locale)
self.en_blog_index = TestPage(title="Blog", slug="blog")
self.en_homepage.add_child(instance=self.en_blog_index)
self.en_blog_post = TestPage(title="Blog post", slug="blog-post")
self.en_blog_index.add_child(instance=self.en_blog_post)
def test_include_subtree(self):
form = SubmitTranslationForm(instance=self.en_blog_post)
self.assertIsInstance(form.fields["include_subtree"].widget, HiddenInput)
form = SubmitTranslationForm(instance=self.en_blog_index)
self.assertIsInstance(form.fields["include_subtree"].widget, CheckboxInput)
self.assertEqual(
form.fields["include_subtree"].label, "Include subtree (1 page)"
)
form = SubmitTranslationForm(instance=self.en_homepage)
self.assertEqual(
form.fields["include_subtree"].label, "Include subtree (2 pages)"
)
def test_locales_queryset(self):
# Homepage is translated to all locales.
form = SubmitTranslationForm(instance=self.en_homepage)
self.assertEqual(
list(
form.fields["locales"].queryset.values_list("language_code", flat=True)
),
[],
)
# Blog index can be translated to `de` and `fr`.
form = SubmitTranslationForm(instance=self.en_blog_index)
self.assertEqual(
list(
form.fields["locales"].queryset.values_list("language_code", flat=True)
),
["de", "fr"],
)
# Blog post can be translated to `de` and `fr`.
form = SubmitTranslationForm(instance=self.en_blog_post)
self.assertEqual(
list(
form.fields["locales"].queryset.values_list("language_code", flat=True)
),
["de", "fr"],
)
def test_select_all(self):
form = SubmitTranslationForm(instance=self.en_homepage)
# Homepage is translated to all locales.
self.assertIsInstance(form.fields["select_all"].widget, HiddenInput)
form = SubmitTranslationForm(instance=self.en_blog_index)
# Blog post can be translated to `de` and `fr`.
self.assertIsInstance(form.fields["select_all"].widget, CheckboxInput)
def test_locale_disabled(self):
form = SubmitTranslationForm(instance=self.en_blog_post)
# The parent (blog_index) is translated to English.
# German and French are disabled.
self.assertEqual(
list(form.fields["locales"].widget.disabled_values),
[self.de_locale.id, self.fr_locale.id],
)
label = f"""
<label class="disabled">
<input type="checkbox" name="None" value="{self.de_locale.id}" disabled>
German
</label>
"""
self.assertInHTML(label, form.fields["locales"].widget.render(None, None))
def test_locale_help_text(self):
# German and French are disabled.
# The help_text is plural
form = SubmitTranslationForm(instance=self.en_blog_post)
help_text = f"""
Some locales are disabled because some parent pages are not translated.
<br>
<a href="/admin/translation/submit/page/{self.en_blog_index.id}/">
Translate the parent pages.
</a>
"""
self.assertHTMLEqual(form.fields["locales"].help_text, help_text)
# Add German translation
self.en_blog_index.copy_for_translation(self.de_locale)
# French is disabled.
# The help_text is singular.
form = SubmitTranslationForm(instance=self.en_blog_post)
help_text = f"""
A locale is disabled because a parent page is not translated.
<br>
<a href="/admin/translation/submit/page/{self.en_blog_index.id}/">
Translate the parent page.
</a>
"""
self.assertHTMLEqual(form.fields["locales"].help_text, help_text)
def test_hide_submit(self):
# German and French are disabled.
# There are no other pages to be translated.
# Submit is hidden.
form = SubmitTranslationForm(instance=self.en_blog_post)
self.assertFalse(form.show_submit)
# A parent is translated
self.en_blog_index.copy_for_translation(self.de_locale)
form = SubmitTranslationForm(instance=self.en_blog_post)
self.assertTrue(form.show_submit)

View File

@@ -0,0 +1,18 @@
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from wagtail.test.utils import TestCase
class TestMigrations(TestCase):
def test_content_type_exists(self):
self.assertTrue(
ContentType.objects.filter(
app_label="simple_translation", model="simpletranslation"
).exists()
)
def test_permission_exists(self):
self.assertTrue(
Permission.objects.filter(codename="submit_translation").exists()
)

View File

@@ -0,0 +1,440 @@
from django.contrib.admin.utils import quote
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.http import Http404
from django.test import RequestFactory, override_settings
from django.urls import reverse
from django.utils.translation import gettext_lazy
from wagtail import hooks
from wagtail.actions.copy_for_translation import ParentNotTranslatedError
from wagtail.contrib.simple_translation.forms import SubmitTranslationForm
from wagtail.contrib.simple_translation.models import after_create_page
from wagtail.contrib.simple_translation.views import (
SubmitPageTranslationView,
SubmitSnippetTranslationView,
SubmitTranslationView,
)
from wagtail.models import Locale, Page
from wagtail.test.i18n.models import TestPage
from wagtail.test.snippets.models import TranslatableSnippet
from wagtail.test.testapp.models import FullFeaturedSnippet
from wagtail.test.utils import TestCase, WagtailTestUtils
@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
)
class TestSubmitTranslationView(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")
self.en_homepage = Page.objects.get(depth=2)
self.factory = RequestFactory()
def test_template_name(self):
self.assertEqual(
SubmitTranslationView.template_name,
"simple_translation/admin/submit_translation.html",
)
def test_title(self):
self.assertEqual(SubmitTranslationView().title, gettext_lazy("Translate"))
self.assertEqual(SubmitTranslationView().get_title(), gettext_lazy("Translate"))
def test_subtitle(self):
view = SubmitTranslationView()
view.object = self.en_homepage
self.assertEqual(view.get_subtitle(), str(self.en_homepage))
def test_get_form(self):
view = SubmitTranslationView()
view.request = self.factory.get("/path/does/not/matter/")
view.object = self.en_homepage
form = view.get_form()
self.assertIsInstance(form, SubmitTranslationForm)
def test_get_success_url(self):
with self.assertRaises(NotImplementedError):
view = SubmitTranslationView()
view.object = self.en_homepage
view.get_success_url()
def test_get_context_data(self, **kwargs):
view = SubmitTranslationView()
view.request = self.factory.get("/path/does/not/matter/")
view.object = self.en_homepage
context = view.get_context_data()
self.assertIn("form", context.keys())
self.assertIsInstance(context["form"], SubmitTranslationForm)
def test_dispatch_as_anon(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_homepage.id,)
)
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, f"/admin/login/?next={url}")
def test_dispatch_as_moderator(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_homepage.id,)
)
user = self.login()
group = Group.objects.get(name="Moderators")
user.groups.add(group)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
def test_dispatch_as_user_with_perm(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_homepage.id,)
)
user = self.login()
permission = Permission.objects.get(codename="submit_translation")
user.user_permissions.add(permission)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
)
class TestSubmitPageTranslationView(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")
self.en_homepage = Page.objects.get(depth=2)
self.fr_homepage = self.en_homepage.copy_for_translation(self.fr_locale)
self.de_homepage = self.en_homepage.copy_for_translation(self.de_locale)
self.en_blog_index = TestPage(title="Blog", slug="blog")
self.en_homepage.add_child(instance=self.en_blog_index)
self.en_blog_post = TestPage(title="Blog post", slug="blog-post")
self.en_blog_index.add_child(instance=self.en_blog_post)
def test_title(self):
self.assertEqual(SubmitPageTranslationView.title, "Translate page")
def test_get_subtitle(self):
view = SubmitPageTranslationView()
view.object = self.en_homepage
self.assertEqual(view.get_subtitle(), "Welcome to your new Wagtail site!")
def test_submit_page_translation_view_test_get(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_blog_index.id,)
)
self.login()
response = self.client.get(url)
assert isinstance(response.context["form"], SubmitTranslationForm)
def test_submit_page_translation_view_test_post_invalid(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_blog_index.id,)
)
self.login()
response = self.client.post(url, {})
assert response.status_code == 200
assert response.context["form"].errors == {
"locales": ["This field is required."]
}
def test_submit_page_translation_view_test_post_single_locale(self):
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_blog_index.id,)
)
de = Locale.objects.get(language_code="de").id
data = {"locales": [de], "include_subtree": True}
self.login()
response = self.client.post(url, data, follow=True)
translated_page = self.en_blog_index.get_translation(de)
self.assertRedirects(
response, reverse("wagtailadmin_pages:edit", args=[translated_page.pk])
)
self.assertIn(
"The page 'Blog' was successfully created in German",
[msg.message for msg in response.context["messages"]],
)
def test_submit_page_translation_view_test_post_multiple_locales(self):
# Needs an extra page to hit recursive function
en_blog_post_sub = Page(title="Blog post sub", slug="blog-post-sub")
self.en_blog_post.add_child(instance=en_blog_post_sub)
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_blog_post.id,)
)
de = Locale.objects.get(language_code="de").id
fr = Locale.objects.get(language_code="fr").id
data = {"locales": [de, fr], "include_subtree": True}
self.login()
with self.assertRaisesMessage(ParentNotTranslatedError, ""):
self.client.post(url, data)
url = reverse(
"simple_translation:submit_page_translation", args=(self.en_blog_index.id,)
)
response = self.client.post(url, data)
assert response.status_code == 302
assert response.url == f"/admin/pages/{self.en_blog_index.get_parent().id}/"
response = self.client.get(response.url) # follow the redirect
assert [msg.message for msg in response.context["messages"]] == [
"The page 'Blog' was successfully created in 2 locales"
]
@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
)
class TestSubmitSnippetTranslationView(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.en_snippet = TranslatableSnippet(text="Hello world", locale=self.en_locale)
self.en_snippet.save()
def test_get_title(self):
view = SubmitSnippetTranslationView()
view.object = self.en_snippet
self.assertEqual(view.get_title(), "Translate translatable snippet")
def test_get_object(self):
view = SubmitSnippetTranslationView()
view.object = self.en_snippet
view.kwargs = {
"app_label": "some_app",
"model_name": "some_model",
"pk": 1,
}
with self.assertRaises(Http404):
view.get_object()
content_type = ContentType.objects.get_for_model(self.en_snippet)
view.kwargs = {
"app_label": content_type.app_label,
"model_name": content_type.model,
"pk": str(self.en_snippet.pk),
}
self.assertEqual(view.get_object(), self.en_snippet)
def test_get_success_url(self):
view = SubmitSnippetTranslationView()
view.object = self.en_snippet
view.kwargs = {
"app_label": "snippetstests",
"model_name": "translatablesnippet",
"pk": 99,
}
self.assertEqual(
view.get_success_url(),
"/admin/snippets/snippetstests/translatablesnippet/edit/99/",
)
def test_get_success_url_for_single_locale(self):
view = SubmitSnippetTranslationView()
view.object = self.en_snippet
view.kwargs = {
"app_label": "snippetstests",
"model_name": "translatablesnippet",
"pk": 99,
}
self.assertEqual(
view.get_success_url(view.object),
reverse(
"wagtailsnippets_snippetstests_translatablesnippet:edit",
args=[view.object.pk],
),
)
def test_get_success_message(self):
view = SubmitSnippetTranslationView()
view.object = self.en_snippet
self.assertEqual(
view.get_success_message(self.fr_locale),
f"Successfully created French for translatable snippet 'TranslatableSnippet object ({self.en_snippet.id})'",
)
@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_I18N_ENABLED=True,
)
class TestSubmitSnippetTranslationWithDraftState(WagtailTestUtils, TestCase):
def setUp(self):
self.login()
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")
self.en_snippet = FullFeaturedSnippet.objects.create(
text="Hello world", locale=self.en_locale, live=False
)
self.en_snippet.save_revision().publish()
self.en_snippet.text = "It's edited"
self.en_snippet.save_revision()
self.en_snippet.refresh_from_db()
self.model_opts = self.en_snippet._meta
self.app_label = self.model_opts.app_label
self.model_name = self.model_opts.model_name
def get_submit_url(self):
return reverse(
"simple_translation:submit_snippet_translation",
args=(self.app_label, self.model_name, quote(self.en_snippet.pk)),
)
def get_snippet_url(self, view, snippet):
return reverse(
snippet.snippet_viewset.get_url_name(view),
args=(quote(snippet.pk),),
)
def test_submit_snippet_translation_view_test_get(self):
response = self.client.get(self.get_submit_url())
self.assertIsInstance(response.context["form"], SubmitTranslationForm)
def test_submit_snippet_translation_view_test_post_invalid(self):
response = self.client.post(self.get_submit_url(), {})
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.context["form"].errors,
{"locales": ["This field is required."]},
)
def test_submit_snippet_translation_view_test_post_single_locale(self):
data = {"locales": [self.de_locale.id], "include_subtree": True}
response = self.client.post(self.get_submit_url(), data, follow=True)
translated_snippet = self.en_snippet.get_translation(self.de_locale.id)
self.assertRedirects(response, self.get_snippet_url("edit", translated_snippet))
self.assertContains(response, "It's edited", count=1)
self.assertContains(response, '<h3 id="status-sidebar-german"', count=1)
self.assertContains(
response,
f'<a href="{self.get_snippet_url("edit", self.en_snippet)}"',
count=1,
)
self.assertNotContains(
response,
f'<a href="{self.get_snippet_url("edit", translated_snippet)}"',
)
self.assertEqual(
[msg.message for msg in response.context["messages"]],
["Successfully created German for full-featured snippet 'It's edited'"],
)
def test_submit_snippet_translation_view_test_post_multiple_locales(self):
url = self.get_submit_url()
data = {"locales": [self.de_locale.id, self.fr_locale.id]}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, self.get_snippet_url("edit", self.en_snippet))
response = self.client.get(response.url) # follow the redirect
self.assertEqual(
[msg.message for msg in response.context["messages"]],
["Successfully created 2 locales for full-featured snippet 'It's edited'"],
)
@override_settings(
LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAIL_CONTENT_LANGUAGES=[
("en", "English"),
("fr", "French"),
("de", "German"),
],
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True,
)
class TestPageTreeSync(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")
self.en_homepage = Page.objects.get(depth=2)
self.fr_homepage = self.en_homepage.copy_for_translation(self.fr_locale)
self.de_homepage = self.en_homepage.copy_for_translation(self.de_locale)
def test_hook_function_registered(self):
fns = hooks.get_hooks("after_create_page")
self.assertIn(after_create_page, fns)
def test_alias_created_after_page_saved(self):
en_blog_index = TestPage(title="Blog", slug="blog")
self.en_homepage.add_child(instance=en_blog_index)
after_create_page(None, en_blog_index)
fr_blog_index = en_blog_index.get_translation(self.fr_locale)
de_blog_index = en_blog_index.get_translation(self.de_locale)
self.assertEqual(fr_blog_index.alias_of.specific, en_blog_index)
self.assertEqual(de_blog_index.alias_of.specific, en_blog_index)
@override_settings(WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=False)
def test_page_sync_disabled(self):
en_blog_index = TestPage(title="Blog", slug="blog")
self.en_homepage.add_child(instance=en_blog_index)
after_create_page(None, en_blog_index)
self.assertFalse(en_blog_index.has_translation(self.fr_locale))
self.assertFalse(en_blog_index.has_translation(self.de_locale))

View File

@@ -0,0 +1,555 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.test import TestCase, override_settings
from django.urls import reverse
from wagtail import hooks
from wagtail.actions.create_alias import CreatePageAliasAction
from wagtail.actions.move_page import MovePageAction
from wagtail.admin import widgets as wagtailadmin_widgets
from wagtail.contrib.simple_translation.wagtail_hooks import (
page_listing_more_buttons,
register_submit_translation_permission,
)
from wagtail.models import Locale, Page
from wagtail.test.i18n.models import TestPage
from wagtail.test.utils import WagtailTestUtils
class Utils(WagtailTestUtils, TestCase):
def setUp(self):
self.en_locale = Locale.objects.first()
self.fr_locale = Locale.objects.create(language_code="fr")
self.de_locale = Locale.objects.create(language_code="de")
self.en_homepage = Page.objects.get(depth=2)
self.fr_homepage = self.en_homepage.copy_for_translation(self.fr_locale)
self.de_homepage = self.en_homepage.copy_for_translation(self.de_locale)
self.en_blog_index = TestPage(title="Blog", slug="blog")
self.en_homepage.add_child(instance=self.en_blog_index)
self.en_blog_post = TestPage(title="Blog post", slug="blog-post")
self.en_blog_index.add_child(instance=self.en_blog_post)
class TestWagtailHooksURLs(TestCase):
def test_register_admin_urls_page(self):
self.assertEqual(
reverse("simple_translation:submit_page_translation", args=(1,)),
"/admin/translation/submit/page/1/",
)
def test_register_admin_urls_snippet(self):
app_label = "foo"
model_name = "bar"
pk = 1
self.assertEqual(
reverse(
"simple_translation:submit_snippet_translation",
args=(app_label, model_name, pk),
),
"/admin/translation/submit/snippet/foo/bar/1/",
)
class TestWagtailHooksPermission(Utils):
def test_register_submit_translation_permission(self):
assert list(
register_submit_translation_permission().values_list("id", flat=True)
) == [
Permission.objects.get(
content_type__app_label="simple_translation",
codename="submit_translation",
).id
]
class TestWagtailHooksButtons(Utils):
def test_page_listing_more_buttons(self):
# Root, no button
root_page = self.en_blog_index.get_root()
if get_user_model().USERNAME_FIELD == "email":
user = get_user_model().objects.create_user(email="jos@example.com")
else:
user = get_user_model().objects.create_user(username="jos")
assert list(page_listing_more_buttons(root_page, user)) == []
# No permissions, no button
home_page = self.en_homepage
assert list(page_listing_more_buttons(root_page, user)) == []
# Homepage is translated to all languages, no button
perm = Permission.objects.get(codename="submit_translation")
if get_user_model().USERNAME_FIELD == "email":
user = get_user_model().objects.create_user(email="henk@example.com")
else:
user = get_user_model().objects.create_user(username="henk")
# New user, to prevent permission cache.
user.user_permissions.add(perm)
group = Group.objects.get(name="Editors")
user.groups.add(group)
assert list(page_listing_more_buttons(home_page, user)) == []
# Page does not have translations yet... button!
blog_page = self.en_blog_post
assert isinstance(
list(page_listing_more_buttons(blog_page, user))[0],
wagtailadmin_widgets.Button,
)
class TestConstructSyncedPageTreeListHook(Utils):
def unpublish_hook(self, pages, action):
self.assertEqual(action, "unpublish")
self.assertIsInstance(pages, list)
def missing_hook_action(self, pages, action):
self.assertEqual(action, "")
self.assertIsInstance(pages, list)
def test_double_registered_hook(self):
# We should have two implementations of `construct_translated_pages_to_cascade_actions`
# One in simple_translation.wagtail_hooks and the other will be
# registered as a temporary hook.
with hooks.register_temporarily(
"construct_translated_pages_to_cascade_actions", self.unpublish_hook
):
defined_hooks = hooks.get_hooks(
"construct_translated_pages_to_cascade_actions"
)
self.assertEqual(len(defined_hooks), 2)
@override_settings(WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True)
def test_page_tree_sync_on(self):
with hooks.register_temporarily(
"construct_translated_pages_to_cascade_actions", self.unpublish_hook
):
for fn in hooks.get_hooks("construct_translated_pages_to_cascade_actions"):
response = fn([self.en_homepage], "unpublish")
if response:
self.assertIsInstance(response, dict)
self.assertEqual(len(response.items()), 1)
@override_settings(WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=False)
def test_page_tree_sync_off(self):
with hooks.register_temporarily(
"construct_translated_pages_to_cascade_actions", self.unpublish_hook
):
for fn in hooks.get_hooks("construct_translated_pages_to_cascade_actions"):
response = fn([self.en_homepage], "unpublish")
self.assertIsNone(response)
@override_settings(WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True)
def test_missing_hook_action(self):
with hooks.register_temporarily(
"construct_translated_pages_to_cascade_actions", self.missing_hook_action
):
for fn in hooks.get_hooks("construct_translated_pages_to_cascade_actions"):
response = fn([self.en_homepage], "")
if response is not None:
self.assertIsInstance(response, dict)
@override_settings(
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True, WAGTAIL_I18N_ENABLED=True
)
def test_other_l10n_pages_were_unpublished(self):
# Login to access the admin
self.login()
# Make sure the French homepage is published/live
self.fr_homepage.live = True
self.fr_homepage.save()
self.assertTrue(self.en_homepage.live)
self.assertTrue(self.fr_homepage.live)
response = self.client.post(
reverse("wagtailadmin_pages:unpublish", args=(self.en_homepage.id,)),
{"include_descendants": False},
follow=True,
)
self.assertEqual(response.status_code, 200)
# Refresh objects from the database
self.en_homepage.refresh_from_db()
self.fr_homepage.refresh_from_db()
# Test that both the English and French homepages are unpublished
self.assertFalse(self.en_homepage.live)
self.assertFalse(self.fr_homepage.live)
class TestMovingTranslatedPages(Utils):
@override_settings(
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True, WAGTAIL_I18N_ENABLED=True
)
def test_move_translated_pages(self):
self.login()
# BlogIndex needs translated pages before child pages can be translated
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
self.de_blog_index = self.en_blog_index.copy_for_translation(self.de_locale)
# Create blog_post copies for translation
self.fr_blog_post = self.en_blog_post.copy_for_translation(self.fr_locale)
self.de_blog_post = self.en_blog_post.copy_for_translation(self.de_locale)
# Confirm location of English blog post page before it is moved
# Should be living at /blog/blog-post/ right now. But will eventually
# exist at /blog-post/
self.assertEqual(self.en_blog_post.get_parent().id, self.en_blog_index.id)
# Check if fr and de blog post parent ids are in the translated list
# This is to make sure the fr blog_post is situated under /fr/blog/
# (same concept with /de/).
# We'll check these after the move to ensure they exist under /fr/ without
# the /blog/ parent page.
original_translated_parent_ids = [
p.id for p in self.en_blog_index.get_translations()
]
self.assertIn(self.fr_blog_post.get_parent().id, original_translated_parent_ids)
self.assertIn(self.de_blog_post.get_parent().id, original_translated_parent_ids)
response = self.client.post(
reverse(
"wagtailadmin_pages:move_confirm",
args=(
self.en_blog_post.id,
self.en_homepage.id,
),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
self.fr_blog_post.refresh_from_db()
self.de_blog_post.refresh_from_db()
# Check if the new pages exist under their respective translated homepages
home_page_translation_ids = [p.id for p in self.en_homepage.get_translations()]
self.assertIn(
self.fr_blog_post.get_parent(update=True).id, home_page_translation_ids
)
self.assertIn(
self.de_blog_post.get_parent(update=True).id, home_page_translation_ids
)
@override_settings(WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=False)
def test_unmovable_translation_pages(self):
"""
Test that moving a page with WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE
disabled doesn't apply to its translations.
"""
self.login()
# BlogIndex needs translated pages before child pages can be translated
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
self.de_blog_index = self.en_blog_index.copy_for_translation(self.de_locale)
# Create blog_post copies for translation
self.fr_blog_post = self.en_blog_post.copy_for_translation(self.fr_locale)
self.de_blog_post = self.en_blog_post.copy_for_translation(self.de_locale)
# Confirm location of English blog post page before it is moved
# Should be living at /blog/blog-post/ right now. But will eventually
# exist at /blog-post/
self.assertEqual(self.en_blog_post.get_parent().id, self.en_blog_index.id)
# Confirm the fr and de blog post pages are under the blog index page
# We'll confirm these have not moved after the POST request.
original_translated_parent_ids = [
p.id for p in self.en_blog_index.get_translations()
]
self.assertIn(self.fr_blog_post.get_parent().id, original_translated_parent_ids)
self.assertIn(self.de_blog_post.get_parent().id, original_translated_parent_ids)
response = self.client.post(
reverse(
"wagtailadmin_pages:move_confirm",
args=(
self.en_blog_post.id,
self.en_homepage.id,
),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
self.en_blog_post.refresh_from_db()
self.fr_blog_post.refresh_from_db()
self.de_blog_post.refresh_from_db()
# Check that the en_blog_post page has moved directly under the home page.
self.assertEqual(
self.en_blog_post.get_parent(update=True).id, self.en_homepage.id
)
# Check if the fr and de pages exist under their original parent page (/blog/)
self.assertIn(
self.fr_blog_post.get_parent(update=True).id, original_translated_parent_ids
)
self.assertIn(
self.de_blog_post.get_parent(update=True).id, original_translated_parent_ids
)
@override_settings(
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True, WAGTAIL_I18N_ENABLED=True
)
def test_translation_count_in_context(self):
"""Test translation count is correct in the confirm_move.html template."""
self.login()
# BlogIndex needs translated pages before child pages can be translated
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
self.de_blog_index = self.en_blog_index.copy_for_translation(self.de_locale)
# create translation in FR tree
self.fr_blog_post = self.en_blog_post.copy_for_translation(self.fr_locale)
# create alias in DE tree
self.de_blog_post = self.en_blog_post.copy_for_translation(
self.de_locale, alias=True
)
response = self.client.get(
reverse(
"wagtailadmin_pages:move_confirm",
args=(
self.en_blog_post.id,
self.en_homepage.id,
),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["translations_to_move_count"], 1)
self.assertIn(
"This will also move one translation of this page and its child pages",
response.content.decode("utf-8"),
)
@override_settings(
WAGTAILSIMPLETRANSLATION_SYNC_PAGE_TREE=True, WAGTAIL_I18N_ENABLED=True
)
class TestDeletingTranslatedPages(Utils):
def delete_hook(self, pages, action):
self.assertEqual(action, "delete")
self.assertIsInstance(pages, list)
def test_construct_translated_pages_to_cascade_actions_when_deleting(self):
with hooks.register_temporarily(
"construct_translated_pages_to_cascade_actions", self.delete_hook
):
for fn in hooks.get_hooks("construct_translated_pages_to_cascade_actions"):
response = fn([self.en_homepage], "delete")
if response is not None:
self.assertIsInstance(response, dict)
self.assertEqual(len(response.items()), 1)
def test_delete_translated_pages(self):
# Login to the Wagtail admin with a superuser account
self.login()
# BlogIndex needs translated pages before child pages can be translated
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
# Create a copy of the en_blog_post object as a translated page
self.fr_blog_post = self.en_blog_post.copy_for_translation(self.fr_locale)
# 1. Delete the en_blog_post by making a POST request to /delete/
response = self.client.post(
reverse(
"wagtailadmin_pages:delete",
args=(self.en_blog_post.id,),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
# 2. Confirm fr_blog_post is deleted
self.assertIsNone(Page.objects.filter(pk=self.fr_blog_post.id).first())
def test_delete_confirmation_template(self):
"""Test the context info is correct in the confirm_delete.html template."""
self.login()
# BlogIndex needs translated pages before child pages can be translated
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
# Create a copy of the en_blog_post object as a translated page
self.fr_blog_post = self.en_blog_post.copy_for_translation(self.fr_locale)
# Create an alias page to test the `translations_to_move_count`
# in the template context
new_page = CreatePageAliasAction(
self.en_blog_post,
recursive=False,
parent=self.en_blog_index,
update_slug="alias-page-slug",
user=None,
)
new_page.execute(skip_permission_checks=True)
response = self.client.get(
reverse(
"wagtailadmin_pages:delete",
args=(self.en_blog_post.id,),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context["translation_count"], 1)
self.assertEqual(response.context["translation_descendant_count"], 0)
self.assertIn(
"Deleting this page will also delete 1 translation of this page.",
response.content.decode("utf-8"),
)
def test_deleting_page_with_divergent_translation_tree(self):
self.login()
# New parent to eventually hold the fr_blog_post object.
self.en_new_parent = TestPage(title="Test Parent", slug="test-parent")
self.en_homepage.add_child(instance=self.en_new_parent)
# Copy the /blog/ and French /blog-post/ pages.
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
self.fr_blog_post = self.en_blog_post.copy_for_translation(self.fr_locale)
# Copy the en new parent to be a french page
self.fr_new_parent = self.en_new_parent.copy_for_translation(self.fr_locale)
# Manually move the fr_blog_post to live under fr_new_parent
# Because this does not go through the POST request in pages/move.py
# this action will create a diverged tree scnenario where en_blog_post
# and fr_blog_post don't mirror their original positions in the tree.
action = MovePageAction(
self.fr_blog_post,
self.fr_new_parent,
pos="last-child",
user=None,
)
action.execute(skip_permission_checks=True)
self.fr_blog_post.refresh_from_db()
self.en_blog_post.refresh_from_db()
# Confirm fr_blog_post parent id is the fr_new_parent id.
# Confirm en_blog_post parent id is the en_blog_index id
self.assertEqual(
self.fr_blog_post.get_parent(update=True).id, self.fr_new_parent.id
)
self.assertEqual(
self.en_blog_post.get_parent(update=True).id, self.en_blog_index.id
)
# Make a post request to move the en_blog_post to live under en_homepage
response = self.client.post(
reverse(
"wagtailadmin_pages:delete",
args=(self.en_blog_post.id,),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
# Confirm that the en_blog_post object no longer exists.
self.assertFalse(Page.objects.filter(pk=self.en_blog_post.id).exists())
# Confirm that the fr_blog_post object stll exists, because it was moved
self.assertTrue(Page.objects.filter(pk=self.fr_blog_post.id).exists())
# Confirm the fr_blog_post parent id matches the new_parent_page id
# This confirms is hasn't moved and hasn't been deleted.
# to a different location in the tree.
self.fr_blog_post.refresh_from_db()
self.assertEqual(
self.fr_blog_post.get_parent(update=True).id, self.fr_new_parent.id
)
def test_alias_pages_when_deleting_source_page(self):
"""
When deleting a page that has an alias page in the same tree, the alias page
should continue to exist while the original page should be deleted
while using the `construct_translated_pages_to_cascade_actions` hook is active.
"""
self.login()
# Test the source page exists in the right tree location
self.assertEqual(self.en_blog_post.get_parent().id, self.en_blog_index.id)
# Create an alias page from en_blog_post
action = CreatePageAliasAction(
self.en_blog_post,
recursive=False,
parent=self.en_blog_index,
update_slug="sample-slug",
user=None,
)
new_page = action.execute(skip_permission_checks=True)
# Make sure the alias page is an alias of the en_blog_post
# and exists under the same parent page.
self.assertEqual(new_page.get_parent().id, self.en_blog_index.id)
# Test alias of source page
self.assertEqual(new_page.alias_of_id, self.en_blog_post.id)
# Delete the en_blog_post page and make sure the alias page is kept in tact.
response = self.client.post(
reverse(
"wagtailadmin_pages:delete",
args=(self.en_blog_post.id,),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
self.assertFalse(Page.objects.filter(pk=self.en_blog_post.id).exists())
self.assertTrue(Page.objects.filter(pk=new_page.id).exists())
def test_translation_alias_pages_when_deleting_source_page(self):
"""
When deleting a page that has an alias page, the alias page
should be deleted while using the `construct_translated_pages_to_cascade_actions`
hook is active.
"""
self.login()
# BlogIndex needs translated pages before child pages can be translated
self.fr_blog_index = self.en_blog_index.copy_for_translation(self.fr_locale)
# Create a copy of the en_blog_post object as a translated alias page
self.fr_blog_post = self.en_blog_post.copy_for_translation(
self.fr_locale, alias=True
)
self.assertEqual(self.fr_blog_post.alias_of_id, self.en_blog_post.id)
self.assertEqual(self.fr_blog_post.get_parent().id, self.fr_blog_index.id)
# Test that the fr_blog_post alias_id is in the list of translations, is a
# proper alias of en_blog_post, and is using the french locale (fr).
# Also check that the page is in the correct language tree
translation_ids = [p.id for p in self.fr_blog_post.get_translations()]
self.assertIn(self.fr_blog_post.alias_of_id, translation_ids)
self.assertEqual(self.fr_blog_post.alias_of_id, self.en_blog_post.id)
self.assertEqual(self.fr_blog_post.locale.language_code, "fr")
# Test the source is in the source tree root (source HomePage)
# Test that the translated alias is in the translated root (fr HomePage)
en_root = Page.objects.filter(depth__gt=1, locale=self.en_locale).first()
fr_root = Page.objects.filter(depth__gt=1, locale=self.fr_locale).first()
self.assertIn(self.en_blog_post, en_root.get_descendants().specific())
self.assertIn(self.fr_blog_post, fr_root.get_descendants().specific())
# Delete the en_blog_post page and make sure the alias page is kept in tact.
response = self.client.post(
reverse(
"wagtailadmin_pages:delete",
args=(self.en_blog_post.id,),
),
follow=True,
)
self.assertEqual(response.status_code, 200)
self.assertFalse(Page.objects.filter(pk=self.en_blog_post.id).exists())
self.assertFalse(Page.objects.filter(pk=self.fr_blog_post.id).exists())
# The source page continues to exist in the source tree root (HomePage)
self.assertNotIn(self.en_blog_post, en_root.get_descendants().specific())
# The alias should no longer be in the translated tree root (fr HomePage)
self.assertNotIn(self.fr_blog_post, fr_root.get_descendants().specific())