initial
This commit is contained in:
0
tests/core/__init__.py
Normal file
0
tests/core/__init__.py
Normal file
153
tests/core/test_accounts.py
Normal file
153
tests/core/test_accounts.py
Normal file
@@ -0,0 +1,153 @@
|
||||
from django.conf import settings as django_settings
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth import authenticate
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.shortcuts import resolve_url
|
||||
from wiki.conf import settings as wiki_settings
|
||||
from wiki.models import reverse
|
||||
|
||||
from ..base import ArticleWebTestUtils
|
||||
from ..base import DjangoClientTestBase
|
||||
from ..base import RequireRootArticleMixin
|
||||
from ..base import SUPERUSER1_PASSWORD
|
||||
from ..base import SUPERUSER1_USERNAME
|
||||
from ..base import TestBase
|
||||
from ..base import wiki_override_settings
|
||||
from ..testdata.models import CustomUser
|
||||
|
||||
|
||||
SIGNUP_TEST_USERNAME = "wiki"
|
||||
SIGNUP_TEST_PASSWORD = "wiki1234567"
|
||||
|
||||
|
||||
class AccountUpdateTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_password_change(self):
|
||||
"""
|
||||
Test that we can make a successful password change via the update form
|
||||
"""
|
||||
# Check out that it works as expected, notice that there is no referrer
|
||||
# on this GET request.
|
||||
self.client.get(
|
||||
resolve_url(
|
||||
"wiki:profile_update",
|
||||
)
|
||||
)
|
||||
|
||||
# Now check that we don't succeed with unmatching passwords
|
||||
example_data = {
|
||||
"password1": "abcdef",
|
||||
"password2": "abcdef123",
|
||||
"email": self.superuser1.email,
|
||||
}
|
||||
|
||||
# save a new revision
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:profile_update"), example_data
|
||||
)
|
||||
self.assertContains(
|
||||
response, "Passwords don", status_code=200
|
||||
) # Django 2/3 output different escaped versions of single quote in don't
|
||||
|
||||
# Now check that we don't succeed with unmatching passwords
|
||||
example_data = {
|
||||
"password1": "abcdef",
|
||||
"password2": "abcdef",
|
||||
"email": self.superuser1.email,
|
||||
}
|
||||
|
||||
# save a new revision
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:profile_update"), example_data
|
||||
)
|
||||
|
||||
# Need to force str() because of:
|
||||
# TypeError: coercing to Unicode: need string or buffer, __proxy__
|
||||
# found
|
||||
self.assertRedirects(response, str(django_settings.LOGIN_REDIRECT_URL))
|
||||
|
||||
self.assertEqual(
|
||||
self.superuser1,
|
||||
authenticate(
|
||||
username=self.superuser1.username,
|
||||
password=example_data["password1"],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class UpdateProfileViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_update_profile(self):
|
||||
self.client.post(
|
||||
resolve_url("wiki:profile_update"),
|
||||
{
|
||||
"email": "test@test.com",
|
||||
"password1": "newPass",
|
||||
"password2": "newPass",
|
||||
},
|
||||
follow=True,
|
||||
)
|
||||
|
||||
test_auth = authenticate(username="admin", password="newPass")
|
||||
|
||||
self.assertIsNotNone(test_auth)
|
||||
self.assertEqual(test_auth.email, "test@test.com")
|
||||
|
||||
|
||||
@wiki_override_settings(ACCOUNT_HANDLING=True)
|
||||
class LogoutViewTests(RequireRootArticleMixin, DjangoClientTestBase):
|
||||
def test_logout_account_handling(self):
|
||||
self.client.get(wiki_settings.LOGOUT_URL)
|
||||
user = auth.get_user(self.client)
|
||||
self.assertIs(auth.get_user(self.client).is_authenticated, False)
|
||||
self.assertIsInstance(user, AnonymousUser)
|
||||
|
||||
|
||||
@wiki_override_settings(ACCOUNT_HANDLING=True)
|
||||
class LoginTestViews(RequireRootArticleMixin, TestBase):
|
||||
def test_already_signed_in(self):
|
||||
self.client.force_login(self.superuser1)
|
||||
response = self.client.get(wiki_settings.LOGIN_URL)
|
||||
self.assertRedirects(response, reverse("wiki:root"))
|
||||
|
||||
def test_log_in(self):
|
||||
self.client.post(
|
||||
wiki_settings.LOGIN_URL,
|
||||
{"username": SUPERUSER1_USERNAME, "password": SUPERUSER1_PASSWORD},
|
||||
)
|
||||
self.assertIs(self.superuser1.is_authenticated, True)
|
||||
self.assertEqual(auth.get_user(self.client), self.superuser1)
|
||||
|
||||
|
||||
class SignupViewTests(RequireRootArticleMixin, TestBase):
|
||||
@wiki_override_settings(ACCOUNT_HANDLING=True, ACCOUNT_SIGNUP_ALLOWED=True)
|
||||
def test_signup(self):
|
||||
response = self.client.post(
|
||||
wiki_settings.SIGNUP_URL,
|
||||
data={
|
||||
"password1": SIGNUP_TEST_PASSWORD,
|
||||
"password2": SIGNUP_TEST_PASSWORD,
|
||||
"username": SIGNUP_TEST_USERNAME,
|
||||
"email": "wiki@wiki.com",
|
||||
},
|
||||
)
|
||||
self.assertIs(
|
||||
CustomUser.objects.filter(email="wiki@wiki.com").exists(), True
|
||||
)
|
||||
self.assertRedirects(response, reverse("wiki:login"))
|
||||
|
||||
# Test that signing up the same user again gives a validation error
|
||||
# Regression test: https://github.com/django-wiki/django-wiki/issues/1152
|
||||
response = self.client.post(
|
||||
wiki_settings.SIGNUP_URL,
|
||||
data={
|
||||
"password1": SIGNUP_TEST_PASSWORD,
|
||||
"password2": SIGNUP_TEST_PASSWORD,
|
||||
"username": SIGNUP_TEST_USERNAME,
|
||||
"email": "wiki@wiki.com",
|
||||
},
|
||||
)
|
||||
self.assertIs(response.status_code, 200)
|
||||
self.assertContains(response, "username already exists")
|
||||
64
tests/core/test_basic.py
Normal file
64
tests/core/test_basic.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
from wiki.conf import settings as wiki_settings
|
||||
from wiki.core.http import send_file
|
||||
from wiki.forms import Group
|
||||
from wiki.models import Article
|
||||
from wiki.models import ArticleRevision
|
||||
from wiki.models import URLPath
|
||||
|
||||
from ..base import wiki_override_settings
|
||||
from ..testdata.models import CustomGroup
|
||||
|
||||
|
||||
class URLPathTests(TestCase):
|
||||
def test_manager(self):
|
||||
root = URLPath.create_root()
|
||||
child = URLPath.create_urlpath(root, "child")
|
||||
|
||||
self.assertIsNone(root.parent)
|
||||
self.assertEqual(list(root.children.all().active()), [child])
|
||||
|
||||
|
||||
class CustomGroupTests(TestCase):
|
||||
@wiki_override_settings(WIKI_GROUP_MODEL="auth.Group")
|
||||
def test_setting(self):
|
||||
self.assertEqual(wiki_settings.GROUP_MODEL, "auth.Group")
|
||||
|
||||
def test_custom(self):
|
||||
self.assertEqual(Group, CustomGroup)
|
||||
self.assertEqual(wiki_settings.GROUP_MODEL, "testdata.CustomGroup")
|
||||
|
||||
|
||||
class LineEndingsTests(TestCase):
|
||||
def test_manager(self):
|
||||
article = Article()
|
||||
article.add_revision(
|
||||
ArticleRevision(title="Root", content="Hello\nworld"), save=True
|
||||
)
|
||||
self.assertEqual("Hello\r\nworld", article.current_revision.content)
|
||||
|
||||
|
||||
class HttpTests(TestCase):
|
||||
def test_send_file(self):
|
||||
fabricate_request = self.client.get("/").wsgi_request
|
||||
fobject = tempfile.NamedTemporaryFile("r")
|
||||
response = send_file(
|
||||
fabricate_request, fobject.name, filename="test.pdf"
|
||||
)
|
||||
assert response.has_header("Content-Disposition")
|
||||
assert "inline" in response.get("Content-Disposition")
|
||||
response = send_file(
|
||||
fabricate_request, fobject.name, filename="test.jpeg"
|
||||
)
|
||||
assert response.has_header("Content-Disposition")
|
||||
response = send_file(
|
||||
fabricate_request,
|
||||
fobject.name,
|
||||
filename="test.jpeg",
|
||||
last_modified=datetime.now(),
|
||||
)
|
||||
assert response.has_header("Content-Disposition")
|
||||
fobject.close()
|
||||
115
tests/core/test_checks.py
Normal file
115
tests/core/test_checks.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import copy
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.checks import Error
|
||||
from django.core.checks import registry
|
||||
from django.test import TestCase
|
||||
from wiki.checks import FIELDS_IN_CUSTOM_USER_MODEL
|
||||
from wiki.checks import REQUIRED_CONTEXT_PROCESSORS
|
||||
from wiki.checks import REQUIRED_INSTALLED_APPS
|
||||
from wiki.checks import Tags
|
||||
|
||||
from ..base import wiki_override_settings
|
||||
|
||||
|
||||
def _remove(settings, arg):
|
||||
return [setting for setting in settings if not setting.startswith(arg)]
|
||||
|
||||
|
||||
class CheckTests(TestCase):
|
||||
def test_required_installed_apps(self):
|
||||
for app in REQUIRED_INSTALLED_APPS:
|
||||
with self.settings(
|
||||
INSTALLED_APPS=_remove(settings.INSTALLED_APPS, app[0])
|
||||
):
|
||||
errors = registry.run_checks(
|
||||
tags=[Tags.required_installed_apps]
|
||||
)
|
||||
expected_errors = [
|
||||
Error(
|
||||
"needs %s in INSTALLED_APPS" % app[1],
|
||||
id="wiki.%s" % app[2],
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected_errors)
|
||||
|
||||
def test_required_context_processors(self):
|
||||
for context_processor in REQUIRED_CONTEXT_PROCESSORS:
|
||||
TEMPLATES = copy.deepcopy(settings.TEMPLATES)
|
||||
TEMPLATES[0]["OPTIONS"]["context_processors"] = [
|
||||
cp
|
||||
for cp in TEMPLATES[0]["OPTIONS"]["context_processors"]
|
||||
if cp != context_processor[0]
|
||||
]
|
||||
with self.settings(TEMPLATES=TEMPLATES):
|
||||
errors = registry.run_checks(tags=[Tags.context_processors])
|
||||
expected_errors = [
|
||||
Error(
|
||||
"needs %s in TEMPLATES[*]['OPTIONS']['context_processors']"
|
||||
% context_processor[0],
|
||||
id="wiki.%s" % context_processor[1],
|
||||
)
|
||||
]
|
||||
self.assertEqual(errors, expected_errors)
|
||||
|
||||
def test_custom_user_model_mitigation_required(self):
|
||||
"""
|
||||
Django check django.forms.ModelForm.Meta on definition, and raises an error if Meta.fields don't exist in Meta.model.
|
||||
This causes problems in wiki.forms.UserCreationForm and wiki.forms.UserUpdateForm when a custom user model doesn't have fields django-wiki assumes.
|
||||
There is some code in wiki.forms that detects this situation.
|
||||
This check asserts that Django are still raising an exception on definition, and asserts the mitigation code in wiki.forms,
|
||||
and that test_check_for_fields_in_custom_user_model below are required.
|
||||
"""
|
||||
from django.core.exceptions import FieldError
|
||||
from django import forms
|
||||
from ..testdata.models import VeryCustomUser
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
FieldError,
|
||||
"Unknown field\\(s\\) \\((email|username|, )+\\) specified for VeryCustomUser",
|
||||
):
|
||||
|
||||
class UserUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = VeryCustomUser
|
||||
fields = ["username", "email"]
|
||||
|
||||
def test_check_for_fields_in_custom_user_model(self):
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
with wiki_override_settings(
|
||||
WIKI_ACCOUNT_HANDLING=False,
|
||||
AUTH_USER_MODEL="testdata.VeryCustomUser",
|
||||
):
|
||||
errors = registry.run_checks(
|
||||
tags=[Tags.fields_in_custom_user_model]
|
||||
)
|
||||
self.assertEqual(errors, [])
|
||||
with wiki_override_settings(
|
||||
WIKI_ACCOUNT_HANDLING=True,
|
||||
AUTH_USER_MODEL="testdata.VeryCustomUser",
|
||||
):
|
||||
errors = registry.run_checks(
|
||||
tags=[Tags.fields_in_custom_user_model]
|
||||
)
|
||||
expected_errors = [
|
||||
Error(
|
||||
"%s.%s.%s refers to a field that is not of type %s"
|
||||
% (
|
||||
get_user_model().__module__,
|
||||
get_user_model().__name__,
|
||||
field_fetcher,
|
||||
required_field_type,
|
||||
),
|
||||
hint="If you have your own login/logout views, turn off settings.WIKI_ACCOUNT_HANDLING",
|
||||
obj=get_user_model(),
|
||||
id="wiki.%s" % error_code,
|
||||
)
|
||||
for check_function_name, field_fetcher, required_field_type, error_code in FIELDS_IN_CUSTOM_USER_MODEL
|
||||
]
|
||||
self.assertEqual(errors, expected_errors)
|
||||
with wiki_override_settings(WIKI_ACCOUNT_HANDLING=True):
|
||||
errors = registry.run_checks(
|
||||
tags=[Tags.fields_in_custom_user_model]
|
||||
)
|
||||
self.assertEqual(errors, [])
|
||||
29
tests/core/test_commands.py
Normal file
29
tests/core/test_commands.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from django.core.management import call_command
|
||||
|
||||
from ..base import ArticleTestBase
|
||||
|
||||
|
||||
class TestManagementCommands(ArticleTestBase):
|
||||
"""
|
||||
This clever test case can be inherited in other plugins
|
||||
|
||||
Some data is created with ArticleTestBase, use that.
|
||||
"""
|
||||
|
||||
def test_dumpdata_loaddata(self):
|
||||
sysout = sys.stdout
|
||||
fixtures_file = tempfile.NamedTemporaryFile(
|
||||
"w", delete=False, suffix=".json"
|
||||
)
|
||||
sys.stdout = fixtures_file
|
||||
call_command("dumpdata", "wiki")
|
||||
fixtures_file.file.flush()
|
||||
fixtures_file.file.close()
|
||||
sys.stdout = open(os.devnull, "w")
|
||||
call_command("loaddata", fixtures_file.name)
|
||||
sys.stdout = sysout
|
||||
os.unlink(fixtures_file.name)
|
||||
81
tests/core/test_editor.py
Normal file
81
tests/core/test_editor.py
Normal file
@@ -0,0 +1,81 @@
|
||||
import wiki.editors
|
||||
from django.forms import Textarea
|
||||
from wiki.editors.base import BaseEditor
|
||||
|
||||
from ..base import RequireRootArticleMixin
|
||||
from ..base import WebTestBase
|
||||
from ..base import wiki_override_settings
|
||||
|
||||
|
||||
class CustomEditor(BaseEditor):
|
||||
def get_widget(self, revision=None):
|
||||
return Textarea(attrs={"data-revision": revision.pk})
|
||||
|
||||
def get_admin_widget(self, revision=None):
|
||||
return Textarea(attrs={"data-revision": revision.pk})
|
||||
|
||||
|
||||
class EditorTest(RequireRootArticleMixin, WebTestBase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# reset the cached editor class and instance
|
||||
wiki.editors._editor, wiki.editors._EditorClass = None, None
|
||||
|
||||
def test_editor_widget_markitup(self):
|
||||
response = self.get_url("wiki:edit", path="")
|
||||
self.assertContains(
|
||||
response,
|
||||
'<textarea name="content" class="markItUp" rows="10" cols="40" id="id_content">',
|
||||
)
|
||||
|
||||
def test_admin_widget_markitup(self):
|
||||
response = self.get_url(
|
||||
"admin:wiki_articlerevision_change",
|
||||
object_id=self.root_article.current_revision.id,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<textarea name="content" class="markItUp" rows="10" cols="40" id="id_content">',
|
||||
)
|
||||
|
||||
@wiki_override_settings(WIKI_EDITOR="wiki.editors.base.BaseEditor")
|
||||
def test_editor_widget_base(self):
|
||||
response = self.get_url("wiki:edit", path="")
|
||||
self.assertContains(
|
||||
response,
|
||||
'<textarea name="content" cols="40" rows="10" id="id_content">',
|
||||
)
|
||||
|
||||
@wiki_override_settings(WIKI_EDITOR="wiki.editors.base.BaseEditor")
|
||||
def test_admin_widget_base(self):
|
||||
response = self.get_url(
|
||||
"admin:wiki_articlerevision_change",
|
||||
object_id=self.root_article.current_revision.id,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<textarea name="content" cols="40" rows="10" id="id_content">',
|
||||
)
|
||||
|
||||
@wiki_override_settings(WIKI_EDITOR="tests.core.test_editor.CustomEditor")
|
||||
def test_editor_widget_custom(self):
|
||||
response = self.get_url("wiki:edit", path="")
|
||||
self.assertContains(
|
||||
response,
|
||||
'<textarea name="content" cols="40" rows="10" data-revision="{}" id="id_content">'.format(
|
||||
self.root_article.current_revision.id
|
||||
),
|
||||
)
|
||||
|
||||
@wiki_override_settings(WIKI_EDITOR="tests.core.test_editor.CustomEditor")
|
||||
def test_admin_widget_custom(self):
|
||||
response = self.get_url(
|
||||
"admin:wiki_articlerevision_change",
|
||||
object_id=self.root_article.current_revision.id,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<textarea name="content" cols="40" rows="10" data-revision="{}" id="id_content">'.format(
|
||||
self.root_article.current_revision.id
|
||||
),
|
||||
)
|
||||
39
tests/core/test_forms.py
Normal file
39
tests/core/test_forms.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.test import TestCase
|
||||
from django.utils.translation import gettext
|
||||
from wiki.forms import DeleteForm
|
||||
from wiki.forms import UserCreationForm
|
||||
|
||||
from tests.base import DjangoClientTestBase
|
||||
from tests.base import RequireRootArticleMixin
|
||||
|
||||
|
||||
class DeleteFormTests(RequireRootArticleMixin, DjangoClientTestBase):
|
||||
def test_not_sure(self):
|
||||
data = {"purge": True, "confirm": False}
|
||||
form = DeleteForm(
|
||||
article=self.root_article, has_children=True, data=data
|
||||
)
|
||||
self.assertIs(form.is_valid(), False)
|
||||
self.assertEqual(
|
||||
form.errors["__all__"], [gettext("You are not sure enough!")]
|
||||
)
|
||||
|
||||
|
||||
class UserCreationFormTests(TestCase):
|
||||
def test_honeypot(self):
|
||||
data = {
|
||||
"address": "Wiki Road 123",
|
||||
"phone": "12345678",
|
||||
"email": "wiki@wiki.com",
|
||||
"username": "WikiMan",
|
||||
"password1": "R@ndomString",
|
||||
"password2": "R@ndomString",
|
||||
}
|
||||
form = UserCreationForm(data=data)
|
||||
self.assertIs(form.is_valid(), False)
|
||||
self.assertEqual(
|
||||
form.errors["__all__"],
|
||||
[
|
||||
"Thank you, non-human visitor. Please keep trying to fill in the form."
|
||||
],
|
||||
)
|
||||
81
tests/core/test_managers.py
Normal file
81
tests/core/test_managers.py
Normal file
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
Tests that the custom queryset methods work, this is important
|
||||
because the pattern of building them is different from Django
|
||||
1.5 to 1.6 to 1.7 so there will be 3 patterns in play at the
|
||||
same time.
|
||||
"""
|
||||
from wiki.models import Article
|
||||
from wiki.models import URLPath
|
||||
from wiki.plugins.attachments.models import Attachment
|
||||
|
||||
from ..base import ArticleTestBase
|
||||
|
||||
|
||||
class ArticleManagerTests(ArticleTestBase):
|
||||
def test_queryset_methods_directly_on_manager(self):
|
||||
self.assertEqual(Article.objects.can_read(self.superuser1).count(), 1)
|
||||
self.assertEqual(Article.objects.can_write(self.superuser1).count(), 1)
|
||||
self.assertEqual(Article.objects.active().count(), 1)
|
||||
|
||||
def test_mass_deletion(self):
|
||||
"""
|
||||
https://github.com/django-wiki/django-wiki/issues/857
|
||||
"""
|
||||
Article.objects.all().delete()
|
||||
self.assertEqual(Article.objects.all().count(), 0)
|
||||
|
||||
def test_queryset_methods_on_querysets(self):
|
||||
self.assertEqual(
|
||||
Article.objects.all().can_read(self.superuser1).count(), 1
|
||||
)
|
||||
self.assertEqual(
|
||||
Article.objects.all().can_write(self.superuser1).count(), 1
|
||||
)
|
||||
self.assertEqual(Article.objects.all().active().count(), 1)
|
||||
|
||||
# See: https://code.djangoproject.com/ticket/22817
|
||||
def test_queryset_empty_querysets(self):
|
||||
self.assertEqual(
|
||||
Article.objects.none().can_read(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
Article.objects.none().can_write(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(Article.objects.none().active().count(), 0)
|
||||
|
||||
|
||||
class AttachmentManagerTests(ArticleTestBase):
|
||||
def test_queryset_methods_directly_on_manager(self):
|
||||
# Do the same for Attachment which uses ArtickeFkManager
|
||||
self.assertEqual(
|
||||
Attachment.objects.can_read(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
Attachment.objects.can_write(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(Attachment.objects.active().count(), 0)
|
||||
|
||||
def test_queryset_methods_on_querysets(self):
|
||||
self.assertEqual(
|
||||
Attachment.objects.all().can_read(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
Attachment.objects.all().can_write(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(Attachment.objects.all().active().count(), 0)
|
||||
|
||||
# See: https://code.djangoproject.com/ticket/22817
|
||||
def test_queryset_empty_query_sets(self):
|
||||
self.assertEqual(
|
||||
Attachment.objects.none().can_read(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
Attachment.objects.none().can_write(self.superuser1).count(), 0
|
||||
)
|
||||
self.assertEqual(Attachment.objects.none().active().count(), 0)
|
||||
|
||||
|
||||
class URLPathManagerTests(ArticleTestBase):
|
||||
def test_related_manager_works_with_filters(self):
|
||||
root = URLPath.root()
|
||||
self.assertNotIn(root.id, [p.id for p in root.children.active()])
|
||||
127
tests/core/test_markdown.py
Normal file
127
tests/core/test_markdown.py
Normal file
@@ -0,0 +1,127 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import markdown
|
||||
from django.test import TestCase
|
||||
from wiki.core.markdown import ArticleMarkdown
|
||||
from wiki.core.markdown.mdx.codehilite import WikiCodeHiliteExtension
|
||||
from wiki.core.markdown.mdx.responsivetable import ResponsiveTableExtension
|
||||
from wiki.models import URLPath
|
||||
|
||||
from ..base import ArticleTestBase
|
||||
|
||||
try:
|
||||
import pygments
|
||||
|
||||
pygments = True # NOQA
|
||||
except ImportError:
|
||||
pygments = False
|
||||
|
||||
|
||||
class ArticleMarkdownTests(ArticleTestBase):
|
||||
@patch("wiki.core.markdown.settings")
|
||||
def test_do_not_modify_extensions(self, settings):
|
||||
extensions = ["footnotes", "attr_list", "sane_lists"]
|
||||
settings.MARKDOWN_KWARGS = {"extensions": extensions}
|
||||
number_of_extensions = len(extensions)
|
||||
ArticleMarkdown(None)
|
||||
self.assertEqual(len(extensions), number_of_extensions)
|
||||
|
||||
def test_html_removal(self):
|
||||
urlpath = URLPath.create_urlpath(
|
||||
self.root,
|
||||
"html_removal",
|
||||
title="Test 1",
|
||||
content="</html>only_this",
|
||||
)
|
||||
|
||||
self.assertEqual(urlpath.article.render(), "<p>only_this</p>")
|
||||
|
||||
|
||||
class ResponsiveTableExtensionTests(TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.md = markdown.Markdown(
|
||||
extensions=["extra", ResponsiveTableExtension()]
|
||||
)
|
||||
self.md_without = markdown.Markdown(extensions=["extra"])
|
||||
|
||||
def test_wrapping(self):
|
||||
text = "|th|th|\n|--|--|\n|td|td|"
|
||||
expected = (
|
||||
'<div class="table-responsive">\n'
|
||||
+ self.md_without.convert(text)
|
||||
+ "\n</div>"
|
||||
)
|
||||
self.assertEqual(self.md.convert(text), expected)
|
||||
|
||||
|
||||
class CodehiliteTests(TestCase):
|
||||
def test_fenced_code(self):
|
||||
md = markdown.Markdown(extensions=["extra", WikiCodeHiliteExtension()])
|
||||
text = (
|
||||
"Code:\n"
|
||||
"\n"
|
||||
"```python\n"
|
||||
"echo 'line 1'\n"
|
||||
"echo 'line 2'\n"
|
||||
"```\n"
|
||||
)
|
||||
result = (
|
||||
(
|
||||
"""<p>Code:</p>\n"""
|
||||
"""<div class="codehilite-wrap"><div class="codehilite"><pre><span></span><span class="n">echo</span> <span class="s1">'line 1'</span>\n"""
|
||||
"""<span class="n">echo</span> <span class="s1">'line 2'</span>\n"""
|
||||
"""</pre></div>\n"""
|
||||
"""</div>"""
|
||||
)
|
||||
if pygments
|
||||
else (
|
||||
"""<p>Code:</p>\n"""
|
||||
"""<div class="codehilite-wrap"><pre class="codehilite"><code class="language-python">echo 'line 1'\n"""
|
||||
"""echo 'line 2'\n</code></pre>\n"""
|
||||
"""</div>"""
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
md.convert(text),
|
||||
result,
|
||||
)
|
||||
|
||||
def test_indented_code(self):
|
||||
md = markdown.Markdown(extensions=["extra", WikiCodeHiliteExtension()])
|
||||
text = (
|
||||
"Code:\n"
|
||||
"\n"
|
||||
" #!/usr/bin/python\n"
|
||||
" print('line 1')\n"
|
||||
" print('line 2')\n"
|
||||
" print('æøå')\n"
|
||||
"\n"
|
||||
)
|
||||
result = (
|
||||
(
|
||||
"""<p>Code:</p>\n"""
|
||||
"""<div class="codehilite-wrap"><table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1\n"""
|
||||
"""2\n"""
|
||||
"""3\n"""
|
||||
"""4</pre></div></td><td class="code"><div class="codehilite"><pre><span></span><span class="ch">#!/usr/bin/python</span>\n"""
|
||||
"""<span class="k">print</span><span class="p">(</span><span class="s1">'line 1'</span><span class="p">)</span>\n"""
|
||||
"""<span class="k">print</span><span class="p">(</span><span class="s1">'line 2'</span><span class="p">)</span>\n"""
|
||||
"""<span class="k">print</span><span class="p">(</span><span class="s1">'æøå'</span><span class="p">)</span>\n"""
|
||||
"""</pre></div>\n"""
|
||||
"""</td></tr></table></div>"""
|
||||
)
|
||||
if pygments
|
||||
else (
|
||||
"""<p>Code:</p>\n"""
|
||||
"""<div class="codehilite-wrap"><pre class="codehilite"><code class="language-python linenums">#!/usr/bin/python\n"""
|
||||
"""print('line 1')\n"""
|
||||
"""print('line 2')\n"""
|
||||
"""print('æøå')\n</code></pre>\n"""
|
||||
"""</div>"""
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
md.convert(text),
|
||||
result,
|
||||
)
|
||||
147
tests/core/test_models.py
Normal file
147
tests/core/test_models.py
Normal file
@@ -0,0 +1,147 @@
|
||||
from django.apps import apps
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
from django.test.testcases import TestCase
|
||||
from django.urls import re_path
|
||||
from wiki.conf import settings
|
||||
from wiki.managers import ArticleManager
|
||||
from wiki.models import Article
|
||||
from wiki.models import ArticleRevision
|
||||
from wiki.models import URLPath
|
||||
from wiki.urls import WikiURLPatterns
|
||||
|
||||
User = get_user_model()
|
||||
Group = apps.get_model(settings.GROUP_MODEL)
|
||||
|
||||
|
||||
class WikiCustomUrlPatterns(WikiURLPatterns):
|
||||
def get_article_urls(self):
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
"^my-wiki/(?P<article_id>[0-9]+)/$",
|
||||
self.article_view_class.as_view(),
|
||||
name="get",
|
||||
),
|
||||
]
|
||||
return urlpatterns
|
||||
|
||||
def get_article_path_urls(self):
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
"^my-wiki/(?P<path>.+/|)$",
|
||||
self.article_view_class.as_view(),
|
||||
name="get",
|
||||
),
|
||||
]
|
||||
return urlpatterns
|
||||
|
||||
|
||||
class ArticleModelTest(TestCase):
|
||||
def test_default_fields_of_empty_article(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
self.assertIsNone(a.current_revision)
|
||||
self.assertIsNone(a.owner)
|
||||
self.assertIsNone(a.group)
|
||||
|
||||
self.assertIsNotNone(a.created)
|
||||
self.assertIsNotNone(a.modified)
|
||||
|
||||
self.assertIsNotNone(a.group_read)
|
||||
self.assertIsNotNone(a.group_write)
|
||||
self.assertIsNotNone(a.other_read)
|
||||
self.assertIsNotNone(a.other_write)
|
||||
|
||||
# XXX maybe redundant test
|
||||
def test_model_manager_class(self):
|
||||
self.assertIsInstance(Article.objects, ArticleManager)
|
||||
|
||||
def test_str_method_if_have_current_revision(self):
|
||||
title = "Test title"
|
||||
|
||||
a = Article.objects.create()
|
||||
ArticleRevision.objects.create(article=a, title=title)
|
||||
|
||||
self.assertEqual(str(a), title)
|
||||
|
||||
def test_str_method_if_dont_have_current_revision(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
expected = "Article without content (1)"
|
||||
|
||||
self.assertEqual(str(a), expected)
|
||||
|
||||
def test_get_absolute_url_if_urlpath_set_is_exists(self):
|
||||
a1 = Article.objects.create()
|
||||
s1 = Site.objects.create(domain="something.com", name="something.com")
|
||||
u1 = URLPath.objects.create(article=a1, site=s1)
|
||||
|
||||
a2 = Article.objects.create()
|
||||
s2 = Site.objects.create(
|
||||
domain="somethingelse.com", name="somethingelse.com"
|
||||
)
|
||||
URLPath.objects.create(
|
||||
article=a2, site=s2, parent=u1, slug="test_slug"
|
||||
)
|
||||
|
||||
url = a2.get_absolute_url()
|
||||
|
||||
expected = "/test_slug/"
|
||||
|
||||
self.assertEqual(url, expected)
|
||||
|
||||
def test_get_absolute_url_if_urlpath_set_is_not_exists(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
url = a.get_absolute_url()
|
||||
|
||||
expected = "/1/"
|
||||
|
||||
self.assertEqual(url, expected)
|
||||
|
||||
def test_article_is_related_to_articlerevision(self):
|
||||
title = "Test title"
|
||||
|
||||
a = Article.objects.create()
|
||||
r = ArticleRevision.objects.create(article=a, title=title)
|
||||
|
||||
self.assertEqual(r.article, a)
|
||||
self.assertIn(r, a.articlerevision_set.all())
|
||||
|
||||
def test_article_is_related_to_owner(self):
|
||||
u = User.objects.create(username="Noman", password="pass")
|
||||
a = Article.objects.create(owner=u)
|
||||
|
||||
self.assertEqual(a.owner, u)
|
||||
self.assertIn(a, u.owned_articles.all())
|
||||
|
||||
def test_article_is_related_to_group(self):
|
||||
g = Group.objects.create()
|
||||
a = Article.objects.create(group=g)
|
||||
|
||||
self.assertEqual(a.group, g)
|
||||
self.assertIn(a, g.article_set.all())
|
||||
|
||||
def test_cache(self):
|
||||
a = Article.objects.create()
|
||||
ArticleRevision.objects.create(
|
||||
article=a, title="test", content="# header"
|
||||
)
|
||||
expected = """<h1 id="wiki-toc-header">header""" """.*</h1>"""
|
||||
# cached content does not exist yet. this will create it
|
||||
self.assertRegex(a.get_cached_content(), expected)
|
||||
# actual cached content test
|
||||
self.assertRegex(a.get_cached_content(), expected)
|
||||
|
||||
def test_articlerevision_presave_signals(self):
|
||||
a = Article.objects.create()
|
||||
ar1 = ArticleRevision(article=a, title="revision1")
|
||||
|
||||
a.add_revision(ar1)
|
||||
self.assertEqual(ar1, a.current_revision)
|
||||
|
||||
ar2 = ArticleRevision(article=a, title="revision2")
|
||||
|
||||
ar2.save()
|
||||
|
||||
self.assertEqual(ar2.previous_revision, ar1)
|
||||
39
tests/core/test_paginator.py
Normal file
39
tests/core/test_paginator.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from django.test import TestCase
|
||||
from wiki.core.paginator import WikiPaginator
|
||||
|
||||
|
||||
class PaginatorTest(TestCase):
|
||||
"""
|
||||
Test the WikiPaginator and it's page_range() function
|
||||
"""
|
||||
|
||||
def test_paginator(self):
|
||||
objects = [1]
|
||||
p = WikiPaginator(objects, 2, side_pages=2)
|
||||
self.assertEqual(p.num_pages, 1)
|
||||
|
||||
p.page(1)
|
||||
self.assertEqual(p.page_range, [1])
|
||||
|
||||
objects = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
p = WikiPaginator(objects, 2, side_pages=2)
|
||||
self.assertEqual(p.num_pages, 5)
|
||||
|
||||
p.page(1)
|
||||
self.assertEqual(p.page_range, [1, 2, 3, 0, 5])
|
||||
|
||||
p.page(3)
|
||||
self.assertEqual(p.page_range, [1, 2, 3, 4, 5])
|
||||
|
||||
objects = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
p = WikiPaginator(objects, 2, side_pages=2)
|
||||
self.assertEqual(p.num_pages, 9)
|
||||
|
||||
p.page(1)
|
||||
self.assertEqual(p.page_range, [1, 2, 3, 0, 9])
|
||||
|
||||
p.page(5)
|
||||
self.assertEqual(p.page_range, [1, 0, 3, 4, 5, 6, 7, 0, 9])
|
||||
|
||||
p.page(8)
|
||||
self.assertEqual(p.page_range, [1, 0, 6, 7, 8, 9])
|
||||
106
tests/core/test_sites.py
Normal file
106
tests/core/test_sites.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from importlib import reload
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
from django.test.testcases import TestCase
|
||||
from django.urls import include
|
||||
from django.urls import re_path
|
||||
from wiki import sites
|
||||
from wiki import urls
|
||||
from wiki.apps import WikiConfig
|
||||
from wiki.models import Article
|
||||
from wiki.models import URLPath
|
||||
|
||||
from ..base import wiki_override_settings
|
||||
|
||||
|
||||
class WikiCustomSite(sites.WikiSite):
|
||||
def get_article_urls(self):
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
"^some-prefix/(?P<article_id>[0-9]+)/$",
|
||||
self.article_view,
|
||||
name="get",
|
||||
),
|
||||
]
|
||||
return urlpatterns
|
||||
|
||||
def get_article_path_urls(self):
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
"^some-other-prefix/(?P<path>.+/|)$",
|
||||
self.article_view,
|
||||
name="get",
|
||||
),
|
||||
]
|
||||
return urlpatterns
|
||||
|
||||
|
||||
class WikiCustomConfig(WikiConfig):
|
||||
default_site = "tests.core.test_sites.WikiCustomSite"
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r"^notify/", include("django_nyt.urls")),
|
||||
re_path(r"", include("wiki.urls")),
|
||||
]
|
||||
|
||||
|
||||
@wiki_override_settings(
|
||||
INSTALLED_APPS=[
|
||||
"tests.testdata",
|
||||
"django.contrib.auth.apps.AuthConfig",
|
||||
"django.contrib.contenttypes.apps.ContentTypesConfig",
|
||||
"django.contrib.sessions.apps.SessionsConfig",
|
||||
"django.contrib.admin.apps.AdminConfig",
|
||||
"django.contrib.humanize.apps.HumanizeConfig",
|
||||
"django.contrib.sites.apps.SitesConfig",
|
||||
"django_nyt.apps.DjangoNytConfig",
|
||||
"mptt",
|
||||
"sekizai",
|
||||
"sorl.thumbnail",
|
||||
"tests.core.test_sites.WikiCustomConfig",
|
||||
"wiki.plugins.attachments.apps.AttachmentsConfig",
|
||||
"wiki.plugins.notifications.apps.NotificationsConfig",
|
||||
"wiki.plugins.images.apps.ImagesConfig",
|
||||
"wiki.plugins.macros.apps.MacrosConfig",
|
||||
"wiki.plugins.globalhistory.apps.GlobalHistoryConfig",
|
||||
],
|
||||
ROOT_URLCONF="tests.core.test_sites",
|
||||
)
|
||||
class CustomWikiSiteTest(TestCase):
|
||||
def setUp(self):
|
||||
# Reload wiki.urls since it may have already been instantiated by another test app.
|
||||
self._old_site = sites.site
|
||||
sites.site = sites.DefaultWikiSite()
|
||||
reload(urls)
|
||||
|
||||
def tearDown(self):
|
||||
sites.site = self._old_site
|
||||
reload(urls)
|
||||
|
||||
def test_use_custom_wiki_site(self):
|
||||
self.assertEqual(sites.site.__class__.__name__, "WikiCustomSite")
|
||||
|
||||
def test_get_absolute_url_if_urlpath_set_is_not_exists__no_root_urlconf(
|
||||
self
|
||||
):
|
||||
a = Article.objects.create()
|
||||
|
||||
self.assertEqual(a.get_absolute_url(), "/some-prefix/1/")
|
||||
|
||||
def test_get_absolute_url_if_urlpath_set_is_exists__no_root_urlconf(self):
|
||||
a1 = Article.objects.create()
|
||||
s1 = Site.objects.create(domain="something.com", name="something.com")
|
||||
u1 = URLPath.objects.create(article=a1, site=s1)
|
||||
|
||||
a2 = Article.objects.create()
|
||||
s2 = Site.objects.create(
|
||||
domain="somethingelse.com", name="somethingelse.com"
|
||||
)
|
||||
URLPath.objects.create(
|
||||
article=a2, site=s2, parent=u1, slug="test_slug"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
a2.get_absolute_url(), "/some-other-prefix/test_slug/"
|
||||
)
|
||||
368
tests/core/test_template_filters.py
Normal file
368
tests/core/test_template_filters.py
Normal file
@@ -0,0 +1,368 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from wiki.models import Article
|
||||
from wiki.models import ArticleRevision
|
||||
from wiki.templatetags.wiki_tags import can_delete
|
||||
from wiki.templatetags.wiki_tags import can_moderate
|
||||
from wiki.templatetags.wiki_tags import can_read
|
||||
from wiki.templatetags.wiki_tags import can_write
|
||||
from wiki.templatetags.wiki_tags import get_content_snippet
|
||||
from wiki.templatetags.wiki_tags import is_locked
|
||||
|
||||
from ..base import TemplateTestCase
|
||||
from ..base import wiki_override_settings
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class GetContentSnippet(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{{ some_content|get_content_snippet:"keyword, max_words" }}
|
||||
"""
|
||||
|
||||
def test_keyword_at_the_end_of_the_content(self):
|
||||
text = "lorem " * 80
|
||||
content = text + " list"
|
||||
expected = (
|
||||
"lorem lorem lorem lorem lorem lorem lorem lorem lorem "
|
||||
"lorem lorem lorem lorem lorem lorem <strong>list</strong>"
|
||||
)
|
||||
|
||||
output = get_content_snippet(content, "list")
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_keyword_at_the_beginning_of_the_content(self):
|
||||
text = "lorem " * 80
|
||||
content = "list " + text
|
||||
expected = (
|
||||
"<strong>list</strong> lorem lorem lorem lorem lorem "
|
||||
"lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem "
|
||||
"lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem "
|
||||
"lorem lorem lorem"
|
||||
)
|
||||
|
||||
output = get_content_snippet(content, "list")
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_whole_content_consists_of_keywords(self):
|
||||
content = "lorem " * 80
|
||||
expected = "<strong>lorem</strong>" + 30 * " <strong>lorem</strong>"
|
||||
|
||||
output = get_content_snippet(content, "lorem")
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_keyword_is_not_in_a_content(self):
|
||||
content = "lorem " * 80
|
||||
expected = (
|
||||
"lorem lorem lorem lorem lorem lorem lorem lorem lorem "
|
||||
"lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem "
|
||||
"lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem lorem"
|
||||
)
|
||||
|
||||
output = get_content_snippet(content, "list")
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_a_few_keywords_in_content(self):
|
||||
text = "lorem " * 80
|
||||
content = "list " + text
|
||||
|
||||
text = "ipsum " * 80
|
||||
content += text + " list "
|
||||
|
||||
text = "dolorum " * 80
|
||||
content += text + " list"
|
||||
|
||||
expected = "<strong>list</strong>" + 30 * " lorem"
|
||||
|
||||
output = get_content_snippet(content, "list")
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
# XXX bug or feature?
|
||||
def test_keyword_is_in_content_and_max_words_is_zero(self):
|
||||
text = "spam " * 800
|
||||
content = text + " list"
|
||||
|
||||
output = get_content_snippet(content, "list", 0)
|
||||
expected = "spam " * 800 + "<strong>list</strong>"
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
# XXX bug or feature?
|
||||
def test_keyword_is_in_content_and_max_words_is_negative(self):
|
||||
text = "spam " * 80
|
||||
content = text + " list"
|
||||
|
||||
output = get_content_snippet(content, "list", -10)
|
||||
expected = "spam " * 75 + "<strong>list</strong>"
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
# XXX bug or feature?
|
||||
def test_keyword_is_not_in_content_and_max_words_is_zero(self):
|
||||
content = "spam " * 15
|
||||
|
||||
output = get_content_snippet(content, "list", 0)
|
||||
expected = ""
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
# XXX bug or feature?
|
||||
def test_keyword_is_not_in_content_and_max_words_is_negative(self):
|
||||
content = "spam " * 15
|
||||
|
||||
output = get_content_snippet(content, "list", -10)
|
||||
expected = "spam spam spam spam spam"
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_no_content(self):
|
||||
content = ""
|
||||
|
||||
output = get_content_snippet(content, "list")
|
||||
|
||||
self.assertEqual(output, "")
|
||||
|
||||
content = " "
|
||||
|
||||
output = get_content_snippet(content, "list")
|
||||
|
||||
self.assertEqual(output, "")
|
||||
|
||||
def test_strip_tags(self):
|
||||
keyword = "maybe"
|
||||
|
||||
content = """
|
||||
I should citate Shakespeare or Byron.
|
||||
Or <a>maybe</a> copy paste from <a href="http://python.org">python</a>
|
||||
or django documentation. Maybe.
|
||||
"""
|
||||
|
||||
expected = (
|
||||
"I should citate Shakespeare or Byron. "
|
||||
"Or <strong>maybe</strong> copy paste from python "
|
||||
"or django documentation. <strong>Maybe.</strong>"
|
||||
)
|
||||
|
||||
output = get_content_snippet(content, keyword, 30)
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_max_words_arg(self):
|
||||
keyword = "eggs"
|
||||
|
||||
content = """
|
||||
knight eggs spam ham eggs guido python eggs circus
|
||||
"""
|
||||
expected = (
|
||||
"knight <strong>eggs</strong> spam ham "
|
||||
"<strong>eggs</strong> guido"
|
||||
)
|
||||
|
||||
output = get_content_snippet(content, keyword, 5)
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
output = get_content_snippet(content, keyword, 0)
|
||||
|
||||
expected = (
|
||||
"knight <strong>eggs</strong> spam ham "
|
||||
"<strong>eggs</strong> guido python <strong>eggs</strong>"
|
||||
)
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
def test_content_case_preserved(self):
|
||||
keyword = "DOlOr"
|
||||
match = "DoLoR"
|
||||
content = "lorem ipsum %s sit amet" % match
|
||||
output = get_content_snippet(content, keyword)
|
||||
self.assertIn(match, output)
|
||||
self.assertNotIn(keyword, output)
|
||||
|
||||
|
||||
class CanRead(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{{ article|can_read:user }}
|
||||
"""
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_READ=lambda *args: True)
|
||||
def test_user_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
u = User.objects.create(username="Nobody", password="pass")
|
||||
|
||||
output = can_read(a, u)
|
||||
self.assertIs(output, True)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("True", output)
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_READ=lambda *args: False)
|
||||
def test_user_dont_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
u = User.objects.create(username="Noman", password="pass")
|
||||
|
||||
output = can_read(a, u)
|
||||
self.assertIs(output, False)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("False", output)
|
||||
|
||||
|
||||
class CanWrite(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{{ article|can_write:user }}
|
||||
"""
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_DELETE=lambda *args: True)
|
||||
def test_user_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
u = User.objects.create(username="Nobody", password="pass")
|
||||
|
||||
output = can_write(a, u)
|
||||
self.assertIs(output, True)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("True", output)
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_WRITE=lambda *args: False)
|
||||
def test_user_dont_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
u = User.objects.create(username="Noman", password="pass")
|
||||
|
||||
output = can_write(a, u)
|
||||
self.assertIs(output, False)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("False", output)
|
||||
|
||||
|
||||
class CanDelete(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{{ article|can_delete:user }}
|
||||
"""
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_DELETE=lambda *args: True)
|
||||
def test_user_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
u = User.objects.create(username="Nobody", password="pass")
|
||||
|
||||
output = can_delete(a, u)
|
||||
self.assertIs(output, True)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("True", output)
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_WRITE=lambda *args: False)
|
||||
def test_user_dont_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
u = User.objects.create(username="Noman", password="pass")
|
||||
|
||||
output = can_delete(a, u)
|
||||
self.assertIs(output, False)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("False", output)
|
||||
|
||||
|
||||
class CanModerate(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{{ article|can_moderate:user }}
|
||||
"""
|
||||
|
||||
@wiki_override_settings(WIKI_CAN_MODERATE=lambda *args: True)
|
||||
def test_user_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
u = User.objects.create(username="Nobody", password="pass")
|
||||
|
||||
output = can_moderate(a, u)
|
||||
self.assertIs(output, True)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("True", output)
|
||||
|
||||
def test_user_dont_have_permission(self):
|
||||
a = Article.objects.create()
|
||||
u = User.objects.create(username="Noman", password="pass")
|
||||
|
||||
output = can_moderate(a, u)
|
||||
self.assertIs(output, False)
|
||||
|
||||
output = self.render({"article": a, "user": u})
|
||||
self.assertIn("False", output)
|
||||
|
||||
|
||||
class IsLocked(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{{ article|is_locked }}
|
||||
"""
|
||||
|
||||
def test_no_current_revision(self):
|
||||
a = Article.objects.create()
|
||||
|
||||
output = is_locked(a)
|
||||
self.assertIsNone(output)
|
||||
|
||||
output = self.render({"article": a})
|
||||
self.assertIn("None", output)
|
||||
|
||||
def test_have_current_revision_and_not_locked(self):
|
||||
a = Article.objects.create()
|
||||
ArticleRevision.objects.create(article=a, locked=False)
|
||||
|
||||
b = Article.objects.create()
|
||||
ArticleRevision.objects.create(article=b)
|
||||
|
||||
output = is_locked(a)
|
||||
self.assertIs(output, False)
|
||||
|
||||
output = is_locked(b)
|
||||
self.assertIs(output, False)
|
||||
|
||||
output = self.render({"article": a})
|
||||
self.assertIn("False", output)
|
||||
|
||||
def test_have_current_revision_and_locked(self):
|
||||
a = Article.objects.create()
|
||||
ArticleRevision.objects.create(article=a, locked=True)
|
||||
|
||||
output = is_locked(a)
|
||||
self.assertIs(output, True)
|
||||
|
||||
output = self.render({"article": a})
|
||||
self.assertIn("True", output)
|
||||
|
||||
|
||||
class PluginEnabled(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{% if "wiki.plugins.attachments"|plugin_enabled %}It is enabled{% endif %}
|
||||
"""
|
||||
|
||||
def test_true(self):
|
||||
output = self.render({})
|
||||
self.assertIn("It is enabled", output)
|
||||
|
||||
|
||||
class WikiSettings(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{% if "ACCOUNT_HANDLING"|wiki_settings %}It is enabled{% endif %}
|
||||
"""
|
||||
|
||||
@wiki_override_settings(WIKI_ACCOUNT_HANDLING=lambda *args: True)
|
||||
def test_setting(self):
|
||||
output = self.render({})
|
||||
self.assertIn("It is enabled", output)
|
||||
353
tests/core/test_template_tags.py
Normal file
353
tests/core/test_template_tags.py
Normal file
@@ -0,0 +1,353 @@
|
||||
"""
|
||||
Almost all test cases covers both tag calling and template using.
|
||||
"""
|
||||
from django.conf import settings as django_settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.http import HttpRequest
|
||||
from wiki.conf import settings
|
||||
from wiki.forms import CreateRootForm
|
||||
from wiki.models import Article
|
||||
from wiki.models import ArticleForObject
|
||||
from wiki.models import ArticleRevision
|
||||
from wiki.templatetags.wiki_tags import article_for_object
|
||||
from wiki.templatetags.wiki_tags import login_url
|
||||
from wiki.templatetags.wiki_tags import wiki_form
|
||||
from wiki.templatetags.wiki_tags import wiki_render
|
||||
|
||||
from ..base import TemplateTestCase
|
||||
|
||||
if not django_settings.configured:
|
||||
django_settings.configure()
|
||||
|
||||
|
||||
# XXX article_for_object accepts context, but not using it
|
||||
class ArticleForObjectTemplatetagTest(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{% article_for_object obj as anything %}
|
||||
{{ anything }}
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
from wiki.templatetags import wiki_tags
|
||||
|
||||
wiki_tags._cache = {}
|
||||
|
||||
def test_obj_arg_is_not_a_django_model(self):
|
||||
from wiki.templatetags import wiki_tags
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
article_for_object({}, "")
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
article_for_object({"request": 100500}, {})
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
self.render({"obj": "tiger!"})
|
||||
|
||||
self.assertEqual(len(wiki_tags._cache), 0)
|
||||
|
||||
def test_obj_is_not_in__cache_and_articleforobject_is_not_exist(self):
|
||||
from wiki.templatetags.wiki_tags import _cache as cache
|
||||
|
||||
obj = Article.objects.create()
|
||||
|
||||
article_for_object({}, obj)
|
||||
|
||||
self.assertIn(obj, cache)
|
||||
self.assertIsNone(cache[obj])
|
||||
self.assertEqual(len(cache), 1)
|
||||
|
||||
self.render({"obj": obj})
|
||||
|
||||
self.assertIn(obj, cache)
|
||||
self.assertIsNone(cache[obj])
|
||||
self.assertEqual(len(cache), 1)
|
||||
|
||||
def test_obj_is_not_in__cache_and_articleforobjec_is_exist(self):
|
||||
from wiki.templatetags.wiki_tags import _cache as cache
|
||||
|
||||
a = Article.objects.create()
|
||||
content_type = ContentType.objects.get_for_model(a)
|
||||
ArticleForObject.objects.create(
|
||||
article=a, content_type=content_type, object_id=1
|
||||
)
|
||||
|
||||
output = article_for_object({}, a)
|
||||
|
||||
self.assertEqual(output, a)
|
||||
self.assertIn(a, cache)
|
||||
self.assertEqual(cache[a], a)
|
||||
self.assertEqual(len(cache), 1)
|
||||
|
||||
self.render({"obj": a})
|
||||
|
||||
self.assertIn(a, cache)
|
||||
self.assertEqual(cache[a], a)
|
||||
self.assertEqual(len(cache), 1)
|
||||
|
||||
def test_obj_in__cache_and_articleforobject_is_not_exist(self):
|
||||
model = Article.objects.create()
|
||||
|
||||
from wiki.templatetags import wiki_tags
|
||||
|
||||
wiki_tags._cache = {model: "spam"}
|
||||
|
||||
article_for_object({}, model)
|
||||
|
||||
self.assertIn(model, wiki_tags._cache)
|
||||
self.assertIsNone(wiki_tags._cache[model])
|
||||
self.assertEqual(len(wiki_tags._cache), 1)
|
||||
|
||||
self.render({"obj": model})
|
||||
|
||||
self.assertIn(model, wiki_tags._cache)
|
||||
self.assertIsNone(wiki_tags._cache[model])
|
||||
self.assertEqual(len(wiki_tags._cache), 1)
|
||||
|
||||
self.assertNotIn("spam", wiki_tags._cache.values())
|
||||
|
||||
def test_obj_in__cache_and_articleforobjec_is_exist(self):
|
||||
article = Article.objects.create()
|
||||
content_type = ContentType.objects.get_for_model(article)
|
||||
ArticleForObject.objects.create(
|
||||
article=article, content_type=content_type, object_id=1
|
||||
)
|
||||
|
||||
from wiki.templatetags import wiki_tags
|
||||
|
||||
wiki_tags._cache = {article: "spam"}
|
||||
|
||||
output = article_for_object({}, article)
|
||||
|
||||
self.assertEqual(output, article)
|
||||
self.assertIn(article, wiki_tags._cache)
|
||||
self.assertEqual(wiki_tags._cache[article], article)
|
||||
|
||||
output = self.render({"obj": article})
|
||||
|
||||
self.assertIn(article, wiki_tags._cache)
|
||||
self.assertEqual(wiki_tags._cache[article], article)
|
||||
|
||||
expected = "Article without content (1)"
|
||||
|
||||
self.assertIn(expected, output)
|
||||
|
||||
|
||||
# TODO manage plugins in template
|
||||
class WikiRenderTest(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{% wiki_render article pc %}
|
||||
"""
|
||||
|
||||
def tearDown(self):
|
||||
from wiki.core.plugins import registry
|
||||
|
||||
registry._cache = {}
|
||||
super().tearDown()
|
||||
|
||||
keys = [
|
||||
"article",
|
||||
"content",
|
||||
"preview",
|
||||
"plugins",
|
||||
"STATIC_URL",
|
||||
"CACHE_TIMEOUT",
|
||||
]
|
||||
|
||||
def test_if_preview_content_is_none(self):
|
||||
# monkey patch
|
||||
from wiki.core.plugins import registry
|
||||
|
||||
registry._cache = {"ham": "spam"}
|
||||
|
||||
article = Article.objects.create()
|
||||
|
||||
output = wiki_render({}, article)
|
||||
|
||||
self.assertCountEqual(self.keys, output)
|
||||
|
||||
self.assertEqual(output["article"], article)
|
||||
self.assertIsNone(output["content"])
|
||||
self.assertIs(output["preview"], False)
|
||||
|
||||
self.assertEqual(output["plugins"], {"ham": "spam"})
|
||||
self.assertEqual(output["STATIC_URL"], django_settings.STATIC_URL)
|
||||
self.assertEqual(output["CACHE_TIMEOUT"], settings.CACHE_TIMEOUT)
|
||||
|
||||
# Additional check
|
||||
self.render({"article": article, "pc": None})
|
||||
|
||||
def test_called_with_preview_content_and_article_have_current_revision(
|
||||
self
|
||||
):
|
||||
article = Article.objects.create()
|
||||
ArticleRevision.objects.create(
|
||||
article=article,
|
||||
title="Test title",
|
||||
content="Some beauty test text",
|
||||
)
|
||||
|
||||
content = (
|
||||
"""This is a normal paragraph\n"""
|
||||
"""\n"""
|
||||
"""Headline\n"""
|
||||
"""========\n"""
|
||||
)
|
||||
|
||||
expected = (
|
||||
"""(?s).*<p>This is a normal paragraph</p>\n"""
|
||||
"""<h1 id="wiki-toc-headline">Headline"""
|
||||
""".*</h1>.*"""
|
||||
)
|
||||
|
||||
# monkey patch
|
||||
from wiki.core.plugins import registry
|
||||
|
||||
registry._cache = {"spam": "eggs"}
|
||||
|
||||
output = wiki_render({}, article, preview_content=content)
|
||||
self.assertCountEqual(self.keys, output)
|
||||
self.assertEqual(output["article"], article)
|
||||
self.assertRegex(output["content"], expected)
|
||||
self.assertIs(output["preview"], True)
|
||||
self.assertEqual(output["plugins"], {"spam": "eggs"})
|
||||
self.assertEqual(output["STATIC_URL"], django_settings.STATIC_URL)
|
||||
self.assertEqual(output["CACHE_TIMEOUT"], settings.CACHE_TIMEOUT)
|
||||
|
||||
output = self.render({"article": article, "pc": content})
|
||||
self.assertRegex(output, expected)
|
||||
|
||||
def test_called_with_preview_content_and_article_dont_have_current_revision(
|
||||
self
|
||||
):
|
||||
article = Article.objects.create()
|
||||
|
||||
content = (
|
||||
"""This is a normal paragraph\n"""
|
||||
"""\n"""
|
||||
"""Headline\n"""
|
||||
"""========\n"""
|
||||
)
|
||||
|
||||
# monkey patch
|
||||
from wiki.core.plugins import registry
|
||||
|
||||
registry._cache = {"spam": "eggs"}
|
||||
|
||||
output = wiki_render({}, article, preview_content=content)
|
||||
|
||||
self.assertCountEqual(self.keys, output)
|
||||
|
||||
self.assertEqual(output["article"], article)
|
||||
|
||||
self.assertMultiLineEqual(output["content"], "")
|
||||
self.assertIs(output["preview"], True)
|
||||
|
||||
self.assertEqual(output["plugins"], {"spam": "eggs"})
|
||||
self.assertEqual(output["STATIC_URL"], django_settings.STATIC_URL)
|
||||
self.assertEqual(output["CACHE_TIMEOUT"], settings.CACHE_TIMEOUT)
|
||||
|
||||
self.render({"article": article, "pc": content})
|
||||
|
||||
|
||||
class WikiFormTest(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{% wiki_form form_obj %}
|
||||
"""
|
||||
|
||||
def test_form_obj_is_not_baseform_instance(self):
|
||||
context = {"test_key": "test_value"}
|
||||
form_obj = "ham"
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
wiki_form(context, form_obj)
|
||||
|
||||
self.assertEqual(context, {"test_key": "test_value"})
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
self.render({"test_key": 100500})
|
||||
|
||||
self.assertEqual(context, {"test_key": "test_value"})
|
||||
|
||||
def test_form_obj_is_baseform_instance(self):
|
||||
context = {"test_key": "test_value"}
|
||||
|
||||
# not by any special reasons, just a form
|
||||
form_obj = CreateRootForm()
|
||||
|
||||
wiki_form(context, form_obj)
|
||||
|
||||
self.assertEqual(context, {"test_key": "test_value", "form": form_obj})
|
||||
|
||||
self.render({"form_obj": form_obj})
|
||||
|
||||
self.assertEqual(context, {"test_key": "test_value", "form": form_obj})
|
||||
|
||||
|
||||
class LoginUrlTest(TemplateTestCase):
|
||||
template = """
|
||||
{% load wiki_tags %}
|
||||
{% login_url as some_url %}
|
||||
{{ some_url }}
|
||||
"""
|
||||
|
||||
def test_no_request_in_context(self):
|
||||
with self.assertRaises(KeyError):
|
||||
login_url({})
|
||||
|
||||
with self.assertRaises(KeyError):
|
||||
self.render({})
|
||||
|
||||
def test_login_url_if_no_query_string_in_request(self):
|
||||
r = HttpRequest()
|
||||
r.META = {}
|
||||
r.path = "best/test/page/ever/"
|
||||
|
||||
output = login_url({"request": r})
|
||||
|
||||
expected = "/_accounts/login/?next=best/test/page/ever/"
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
output = self.render({"request": r})
|
||||
|
||||
self.assertIn(expected, output)
|
||||
|
||||
def test_login_url_if_query_string_is_empty(self):
|
||||
r = HttpRequest()
|
||||
r.META = {"QUERY_STRING": ""}
|
||||
r.path = "best/test/page/ever/"
|
||||
|
||||
output = login_url({"request": r})
|
||||
|
||||
expected = "/_accounts/login/?next=best/test/page/ever/"
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
output = self.render({"request": r})
|
||||
|
||||
self.assertIn(expected, output)
|
||||
|
||||
def test_login_url_if_query_string_is_not_empty(self):
|
||||
r = HttpRequest()
|
||||
r.META = {"QUERY_STRING": "title=Main_page&action=raw"}
|
||||
r.path = "best/test/page/ever/"
|
||||
|
||||
context = {"request": r}
|
||||
|
||||
output = login_url(context)
|
||||
|
||||
expected = (
|
||||
"/_accounts/login/"
|
||||
"?next=best/test/page/ever/%3Ftitle%3DMain_page%26action%3Draw"
|
||||
)
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
output = self.render({"request": r})
|
||||
|
||||
self.assertIn(expected, output)
|
||||
78
tests/core/test_urls.py
Normal file
78
tests/core/test_urls.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.sites.models import Site
|
||||
from django.test.testcases import TestCase
|
||||
from django.urls import include
|
||||
from django.urls import re_path
|
||||
from wiki.models import Article
|
||||
from wiki.models import URLPath
|
||||
from wiki.urls import get_pattern as get_wiki_pattern
|
||||
from wiki.urls import WikiURLPatterns
|
||||
|
||||
from ..base import wiki_override_settings
|
||||
|
||||
|
||||
class WikiCustomUrlPatterns(WikiURLPatterns):
|
||||
def get_article_urls(self):
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
"^some-prefix/(?P<article_id>[0-9]+)/$",
|
||||
self.article_view_class.as_view(),
|
||||
name="get",
|
||||
),
|
||||
]
|
||||
return urlpatterns
|
||||
|
||||
def get_article_path_urls(self):
|
||||
urlpatterns = [
|
||||
re_path(
|
||||
"^some-other-prefix/(?P<path>.+/|)$",
|
||||
self.article_view_class.as_view(),
|
||||
name="get",
|
||||
),
|
||||
]
|
||||
return urlpatterns
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r"^notify/", include("django_nyt.urls")),
|
||||
re_path(
|
||||
r"^elsewhere/",
|
||||
get_wiki_pattern(url_config_class=WikiCustomUrlPatterns),
|
||||
),
|
||||
] + static("/static/", document_root="./")
|
||||
|
||||
|
||||
@wiki_override_settings(
|
||||
WIKI_URL_CONFIG_CLASS="tests.core.test_models.WikiCustomUrlPatterns",
|
||||
ROOT_URLCONF="tests.core.test_urls",
|
||||
)
|
||||
class ArticleModelReverseMethodTest(TestCase):
|
||||
def test_get_absolute_url_if_urlpath_set_is_not_exists__no_root_urlconf(
|
||||
self
|
||||
):
|
||||
a = Article.objects.create()
|
||||
|
||||
url = a.get_absolute_url()
|
||||
|
||||
expected = "/elsewhere/some-prefix/1/"
|
||||
|
||||
self.assertEqual(url, expected)
|
||||
|
||||
def test_get_absolute_url_if_urlpath_set_is_exists__no_root_urlconf(self):
|
||||
a1 = Article.objects.create()
|
||||
s1 = Site.objects.create(domain="something.com", name="something.com")
|
||||
u1 = URLPath.objects.create(article=a1, site=s1)
|
||||
|
||||
a2 = Article.objects.create()
|
||||
s2 = Site.objects.create(
|
||||
domain="somethingelse.com", name="somethingelse.com"
|
||||
)
|
||||
URLPath.objects.create(
|
||||
article=a2, site=s2, parent=u1, slug="test_slug"
|
||||
)
|
||||
|
||||
url = a2.get_absolute_url()
|
||||
|
||||
expected = "/elsewhere/some-other-prefix/test_slug/"
|
||||
|
||||
self.assertEqual(url, expected)
|
||||
14
tests/core/test_utils.py
Normal file
14
tests/core/test_utils.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from django.test import TestCase
|
||||
from wiki.core.utils import object_to_json_response
|
||||
|
||||
|
||||
class TestUtils(TestCase):
|
||||
def test_object_to_json(self):
|
||||
"""
|
||||
Simple test, the actual serialization happens in json.dumps and we
|
||||
don't wanna test this core module in depth.
|
||||
"""
|
||||
obj = []
|
||||
response = object_to_json_response(obj)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"[]")
|
||||
982
tests/core/test_views.py
Normal file
982
tests/core/test_views.py
Normal file
@@ -0,0 +1,982 @@
|
||||
import pprint
|
||||
|
||||
from django.contrib.messages import constants
|
||||
from django.contrib.messages import get_messages
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import resolve_url
|
||||
from django.test import override_settings
|
||||
from django.utils import translation
|
||||
from django.utils.html import escape
|
||||
from django_functest import FuncBaseMixin
|
||||
from wiki import models
|
||||
from wiki.conf import settings as wiki_settings
|
||||
from wiki.forms import PermissionsForm
|
||||
from wiki.forms import validate_slug_numbers
|
||||
from wiki.models import ArticleRevision
|
||||
from wiki.models import reverse
|
||||
from wiki.models import URLPath
|
||||
|
||||
from ..base import ArticleWebTestUtils
|
||||
from ..base import DjangoClientTestBase
|
||||
from ..base import NORMALUSER1_PASSWORD
|
||||
from ..base import NORMALUSER1_USERNAME
|
||||
from ..base import RequireRootArticleMixin
|
||||
from ..base import SeleniumBase
|
||||
from ..base import SUPERUSER1_USERNAME
|
||||
from ..base import WebTestBase
|
||||
from tests.testdata.models import CustomGroup
|
||||
|
||||
|
||||
class RootArticleViewTestsBase(FuncBaseMixin):
|
||||
|
||||
"""Tests for creating/viewing the root article."""
|
||||
|
||||
def test_root_article(self):
|
||||
"""
|
||||
Test redirecting to /create-root/,
|
||||
creating the root article and a simple markup.
|
||||
"""
|
||||
self.get_url("wiki:root")
|
||||
self.assertUrlsEqual(resolve_url("wiki:root_create"))
|
||||
self.fill(
|
||||
{
|
||||
"#id_content": "test heading h1\n====\n",
|
||||
"#id_title": "Wiki Test",
|
||||
}
|
||||
)
|
||||
self.submit('button[name="save_changes"]')
|
||||
self.assertUrlsEqual("/")
|
||||
self.assertTextPresent("test heading h1")
|
||||
article = URLPath.root().article
|
||||
self.assertIn("test heading h1", article.current_revision.content)
|
||||
|
||||
|
||||
class RootArticleViewTestsWebTest(RootArticleViewTestsBase, WebTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class RootArticleViewTestsSelenium(RootArticleViewTestsBase, SeleniumBase):
|
||||
pass
|
||||
|
||||
|
||||
class ArticleViewViewTests(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
|
||||
"""
|
||||
Tests for article views, assuming a root article already created.
|
||||
"""
|
||||
|
||||
def dump_db_status(self, message=""):
|
||||
"""Debug printing of the complete important database content."""
|
||||
|
||||
print(f"*** db status *** {message}")
|
||||
|
||||
from wiki.models import Article, ArticleRevision
|
||||
|
||||
for klass in (Article, ArticleRevision, URLPath):
|
||||
print(f"* {klass.__name__} *")
|
||||
pprint.pprint(list(klass.objects.values()), width=240)
|
||||
|
||||
def test_redirects_to_create_if_the_slug_is_unknown(self):
|
||||
response = self.get_by_path("unknown/")
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:create", path="") + "?slug=unknown"
|
||||
)
|
||||
|
||||
def test_redirects_to_create_with_lowercased_slug(self):
|
||||
response = self.get_by_path("Unknown_Linked_Page/")
|
||||
self.assertRedirects(
|
||||
response,
|
||||
resolve_url("wiki:create", path="") + "?slug=unknown_linked_page",
|
||||
)
|
||||
|
||||
def test_article_list_update(self):
|
||||
"""
|
||||
Test automatic adding and removing the new article to/from article_list.
|
||||
"""
|
||||
|
||||
root_data = {
|
||||
"content": "[article_list depth:2]",
|
||||
"current_revision": str(
|
||||
URLPath.root().article.current_revision.id
|
||||
),
|
||||
"preview": "1",
|
||||
"title": "Root Article",
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:edit", path=""), root_data
|
||||
)
|
||||
self.assertRedirects(response, resolve_url("wiki:root"))
|
||||
|
||||
# verify the new article is added to article_list
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Sub Article 1", "slug": "SubArticle1"},
|
||||
)
|
||||
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="subarticle1/")
|
||||
)
|
||||
self.assertContains(self.get_by_path(""), "Sub Article 1")
|
||||
self.assertContains(self.get_by_path(""), "subarticle1/")
|
||||
|
||||
# verify the deleted article is removed from article_list
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:delete", path="SubArticle1/"),
|
||||
{
|
||||
"confirm": "on",
|
||||
"purge": "on",
|
||||
"revision": str(
|
||||
URLPath.objects.get(
|
||||
slug="subarticle1"
|
||||
).article.current_revision.id
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(response, resolve_url("wiki:get", path=""))
|
||||
messages = [m.message for m in get_messages(response.wsgi_request)]
|
||||
self.assertIn(
|
||||
"This article together with all its contents are now completely gone",
|
||||
messages[0],
|
||||
)
|
||||
self.assertNotContains(self.get_by_path(""), "Sub Article 1")
|
||||
|
||||
def test_anonymous_root(self):
|
||||
self.client.logout()
|
||||
response = self.client.get(
|
||||
reverse("wiki:get", kwargs={"article_id": self.root_article.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(reverse("wiki:get", kwargs={"path": ""}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_normaluser_root(self):
|
||||
self.client.login(
|
||||
username=NORMALUSER1_USERNAME, password=NORMALUSER1_PASSWORD
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse("wiki:get", kwargs={"article_id": self.root_article.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(reverse("wiki:get", kwargs={"path": ""}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_show_max_children(self):
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Main",
|
||||
"slug": "WikiRoot",
|
||||
"content": "Content level 1",
|
||||
},
|
||||
)
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="wikiroot/")
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse("wiki:get", kwargs={"path": "wikiroot/"})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIsInstance(response.context["children_slice"], list)
|
||||
self.assertEqual(len(response.context["children_slice"]), 0)
|
||||
for idx in range(1, 40):
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path="wikiroot/"),
|
||||
{
|
||||
"title": f"Sub Article {idx}",
|
||||
"slug": f"SubArticle{idx}",
|
||||
"content": f"Sub Article {idx}",
|
||||
},
|
||||
)
|
||||
self.assertRedirects(
|
||||
response,
|
||||
resolve_url("wiki:get", path=f"wikiroot/subarticle{idx}/"),
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse("wiki:get", kwargs={"path": "wikiroot/"})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(
|
||||
len(response.context["children_slice"]),
|
||||
wiki_settings.SHOW_MAX_CHILDREN,
|
||||
)
|
||||
|
||||
|
||||
class CreateViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_create_nested_article_in_article(self):
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Level 1",
|
||||
"slug": "Level1",
|
||||
"content": "Content level 1",
|
||||
},
|
||||
)
|
||||
self.assertRedirects(response, resolve_url("wiki:get", path="level1/"))
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path="Level1/"),
|
||||
{"title": "test", "slug": "Test", "content": "Content on level 2"},
|
||||
)
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="level1/test/")
|
||||
)
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "test",
|
||||
"slug": "Test",
|
||||
"content": "Other content on level 1",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(response, resolve_url("wiki:get", path="test/"))
|
||||
self.assertContains(
|
||||
self.get_by_path("Test/"), "Other content on level 1"
|
||||
)
|
||||
self.assertContains(
|
||||
self.get_by_path("Level1/Test/"), "Content"
|
||||
) # on level 2')
|
||||
|
||||
def test_illegal_slug(self):
|
||||
# A slug cannot be '123' because it gets confused with an article ID.
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Illegal slug", "slug": "123", "content": "blah"},
|
||||
)
|
||||
self.assertContains(response, escape(validate_slug_numbers.message))
|
||||
|
||||
|
||||
class MoveViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_illegal_slug(self):
|
||||
# A slug cannot be '123' because it gets confused with an article ID.
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:move", path=""),
|
||||
{"destination": "", "slug": "123", "redirect": ""},
|
||||
)
|
||||
self.assertContains(response, escape(validate_slug_numbers.message))
|
||||
|
||||
def test_move(self):
|
||||
# Create a hierarchy of pages
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Test", "slug": "test0", "content": "Content .0."},
|
||||
)
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path="test0/"),
|
||||
{"title": "Test00", "slug": "test00", "content": "Content .00."},
|
||||
)
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Test1", "slug": "test1", "content": "Content .1."},
|
||||
)
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path="test1/"),
|
||||
{"title": "Tes10", "slug": "test10", "content": "Content .10."},
|
||||
)
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path="test1/test10/"),
|
||||
{
|
||||
"title": "Test100",
|
||||
"slug": "test100",
|
||||
"content": "Content .100.",
|
||||
},
|
||||
)
|
||||
|
||||
# Move /test1 => /test0 (an already existing destination slug!)
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:move", path="test1/"),
|
||||
{
|
||||
"destination": str(URLPath.root().article.current_revision.id),
|
||||
"slug": "test0",
|
||||
"redirect": "",
|
||||
},
|
||||
)
|
||||
self.assertContains(response, "A slug named")
|
||||
self.assertContains(response, "already exists.")
|
||||
|
||||
# Move /test1 >= /test2 (valid slug), no redirect
|
||||
test0_id = URLPath.objects.get(
|
||||
slug="test0"
|
||||
).article.current_revision.id
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:move", path="test1/"),
|
||||
{"destination": str(test0_id), "slug": "test2", "redirect": ""},
|
||||
)
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="test0/test2/")
|
||||
)
|
||||
|
||||
# Check that there is no article displayed in this path anymore
|
||||
response = self.get_by_path("test1/")
|
||||
self.assertRedirects(response, "/_create/?slug=test1")
|
||||
|
||||
# Create /test0/test2/test020
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path="test0/test2/"),
|
||||
{
|
||||
"title": "Test020",
|
||||
"slug": "test020",
|
||||
"content": "Content .020.",
|
||||
},
|
||||
)
|
||||
# Move /test0/test2 => /test1new + create redirect
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:move", path="test0/test2/"),
|
||||
{
|
||||
"destination": str(URLPath.root().article.current_revision.id),
|
||||
"slug": "test1new",
|
||||
"redirect": "true",
|
||||
},
|
||||
)
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="test1new/")
|
||||
)
|
||||
|
||||
# Check that /test1new is a valid path
|
||||
response = self.get_by_path("test1new/")
|
||||
self.assertContains(response, "Content .1.")
|
||||
|
||||
# Check that the child article test0/test2/test020 was also moved
|
||||
response = self.get_by_path("test1new/test020/")
|
||||
self.assertContains(response, "Content .020.")
|
||||
|
||||
response = self.get_by_path("test0/test2/")
|
||||
self.assertContains(response, "Moved: Test1")
|
||||
self.assertRegex(
|
||||
response.rendered_content, r"moved to <a[^>]*>wiki:/test1new/"
|
||||
)
|
||||
|
||||
response = self.get_by_path("test0/test2/test020/")
|
||||
self.assertContains(response, "Moved: Test020")
|
||||
self.assertRegex(
|
||||
response.rendered_content,
|
||||
r"moved to <a[^>]*>wiki:/test1new/test020",
|
||||
)
|
||||
|
||||
# Check that moved_to was correctly set
|
||||
urlsrc = URLPath.get_by_path("/test0/test2/")
|
||||
urldst = URLPath.get_by_path("/test1new/")
|
||||
self.assertEqual(urlsrc.moved_to, urldst)
|
||||
|
||||
# Check that moved_to was correctly set on the child's previous path
|
||||
urlsrc = URLPath.get_by_path("/test0/test2/test020/")
|
||||
urldst = URLPath.get_by_path("/test1new/test020/")
|
||||
self.assertEqual(urlsrc.moved_to, urldst)
|
||||
|
||||
def test_translation(self):
|
||||
# Test that translation of "Be careful, links to this article" exists.
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Test", "slug": "test0", "content": "Content"},
|
||||
)
|
||||
url = resolve_url("wiki:move", path="test0/")
|
||||
response_en = self.client.get(url)
|
||||
self.assertIn("Move article", response_en.rendered_content)
|
||||
self.assertIn("Be careful", response_en.rendered_content)
|
||||
|
||||
with translation.override("da-DK"):
|
||||
response_da = self.client.get(url)
|
||||
|
||||
self.assertNotIn("Move article", response_da.rendered_content)
|
||||
self.assertNotIn("Be careful", response_da.rendered_content)
|
||||
|
||||
|
||||
class DeleteViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_render_delete_view(self):
|
||||
"""
|
||||
Other tests do not render the delete view but just sends a POST
|
||||
"""
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Test delete",
|
||||
"slug": "testdelete",
|
||||
"content": "To be deleted",
|
||||
},
|
||||
)
|
||||
response = self.client.get(
|
||||
resolve_url("wiki:delete", path="testdelete/"),
|
||||
)
|
||||
# test the cache
|
||||
self.assertContains(response, "Delete article")
|
||||
|
||||
def test_articles_cache_is_cleared_after_deleting(self):
|
||||
# That bug is tested by one individual test, otherwise it could be
|
||||
# revealed only by sequence of tests in some particular order
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Test cache",
|
||||
"slug": "testcache",
|
||||
"content": "Content 1",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="testcache/")
|
||||
)
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:delete", path="testcache/"),
|
||||
{
|
||||
"confirm": "on",
|
||||
"purge": "on",
|
||||
"revision": str(
|
||||
URLPath.objects.get(
|
||||
slug="testcache"
|
||||
).article.current_revision.id
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(response, resolve_url("wiki:get", path=""))
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Test cache",
|
||||
"slug": "TestCache",
|
||||
"content": "Content 2",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="testcache/")
|
||||
)
|
||||
self.assertContains(self.get_by_path("TestCache/"), "Content 2")
|
||||
|
||||
def test_deleted_view(self):
|
||||
"""
|
||||
Test that a special page is shown for restoring/purging a deleted
|
||||
article.
|
||||
"""
|
||||
# 1. Create the article
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Test delete",
|
||||
"slug": "testdelete",
|
||||
"content": "To be deleted",
|
||||
},
|
||||
)
|
||||
# 2. Soft delete it
|
||||
self.client.post(
|
||||
resolve_url("wiki:delete", path="testdelete/"),
|
||||
{
|
||||
"confirm": "on",
|
||||
"purge": "",
|
||||
"revision": str(
|
||||
URLPath.objects.get(
|
||||
slug="testdelete"
|
||||
).article.current_revision.id
|
||||
),
|
||||
},
|
||||
)
|
||||
# 3. Get and test that it redirects to the deleted page
|
||||
response = self.client.get(
|
||||
resolve_url("wiki:get", path="testdelete/"),
|
||||
follow=True,
|
||||
)
|
||||
# test that it's the Deleted page
|
||||
self.assertContains(response, "Article deleted")
|
||||
|
||||
# 4. Test that we can purge the page now
|
||||
self.client.post(
|
||||
resolve_url("wiki:deleted", path="testdelete/"),
|
||||
{
|
||||
"confirm": "on",
|
||||
"purge": "on",
|
||||
"revision": str(
|
||||
URLPath.objects.get(
|
||||
slug="testdelete"
|
||||
).article.current_revision.id
|
||||
),
|
||||
},
|
||||
)
|
||||
# 5. Test that it's not found anymore
|
||||
response = self.client.get(
|
||||
resolve_url("wiki:get", path="testdelete/"),
|
||||
follow=True,
|
||||
)
|
||||
self.assertContains(response, "Add new article")
|
||||
|
||||
# def test_delete_article_without_urlpath(self):
|
||||
# """
|
||||
# We need a test that tests that articles without URLpaths are correctly
|
||||
# deleted.
|
||||
# """
|
||||
# pass
|
||||
|
||||
# def test_dont_delete_children(self):
|
||||
# Article.objects.create()
|
||||
|
||||
|
||||
class EditViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_preview_save(self):
|
||||
"""Test edit preview, edit save and messages."""
|
||||
|
||||
example_data = {
|
||||
"content": "The modified text",
|
||||
"current_revision": str(
|
||||
URLPath.root().article.current_revision.id
|
||||
),
|
||||
"preview": "1",
|
||||
# 'save': '1', # probably not too important
|
||||
"summary": "why edited",
|
||||
"title": "wiki test",
|
||||
}
|
||||
|
||||
# test preview
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:preview", path=""),
|
||||
example_data, # url: '/_preview/'
|
||||
)
|
||||
|
||||
self.assertContains(response, "The modified text")
|
||||
|
||||
def test_preview_xframe_options_sameorigin(self):
|
||||
"""Ensure that preview response has X-Frame-Options: SAMEORIGIN"""
|
||||
|
||||
example_data = {
|
||||
"content": "The modified text",
|
||||
"current_revision": str(
|
||||
URLPath.root().article.current_revision.id
|
||||
),
|
||||
"preview": "1",
|
||||
"summary": "why edited",
|
||||
"title": "wiki test",
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:preview", path=""), example_data
|
||||
)
|
||||
|
||||
self.assertEqual(response.get("X-Frame-Options"), "SAMEORIGIN")
|
||||
|
||||
def test_revision_conflict(self):
|
||||
"""
|
||||
Test the warning if the same article is being edited concurrently.
|
||||
"""
|
||||
|
||||
example_data = {
|
||||
"content": "More modifications",
|
||||
"current_revision": str(
|
||||
URLPath.root().article.current_revision.id
|
||||
),
|
||||
"preview": "0",
|
||||
"save": "1",
|
||||
"summary": "why edited",
|
||||
"title": "wiki test",
|
||||
}
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:edit", path=""), example_data
|
||||
)
|
||||
|
||||
self.assertRedirects(response, resolve_url("wiki:root"))
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:edit", path=""), example_data
|
||||
)
|
||||
|
||||
self.assertContains(
|
||||
response,
|
||||
"While you were editing, someone else changed the revision.",
|
||||
)
|
||||
|
||||
|
||||
class DiffViewTests(RequireRootArticleMixin, DjangoClientTestBase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.root_article.add_revision(
|
||||
ArticleRevision(title="New Revision"), save=True
|
||||
)
|
||||
self.new_revision = self.root_article.current_revision
|
||||
|
||||
def test_diff(self):
|
||||
response = self.client.get(
|
||||
reverse("wiki:diff", kwargs={"revision_id": self.root_article.pk})
|
||||
)
|
||||
diff = {
|
||||
"diff": ["+ root article content"],
|
||||
"other_changes": [["New title", "Root Article"]],
|
||||
}
|
||||
self.assertJSONEqual(str(response.content, encoding="utf8"), diff)
|
||||
self.assertIsInstance(response, JsonResponse)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class EditViewTestsBase(RequireRootArticleMixin, FuncBaseMixin):
|
||||
def test_edit_save(self):
|
||||
old_revision = URLPath.root().article.current_revision
|
||||
self.get_url("wiki:edit", path="")
|
||||
self.fill(
|
||||
{
|
||||
"#id_content": "Something 2",
|
||||
"#id_summary": "why edited",
|
||||
"#id_title": "wiki test",
|
||||
}
|
||||
)
|
||||
self.submit("#id_save")
|
||||
self.assertTextPresent("Something 2")
|
||||
self.assertTextPresent("successfully added")
|
||||
new_revision = URLPath.root().article.current_revision
|
||||
self.assertIn("Something 2", new_revision.content)
|
||||
self.assertEqual(
|
||||
new_revision.revision_number, old_revision.revision_number + 1
|
||||
)
|
||||
|
||||
|
||||
class EditViewTestsWebTest(EditViewTestsBase, WebTestBase):
|
||||
pass
|
||||
|
||||
|
||||
class EditViewTestsSelenium(EditViewTestsBase, SeleniumBase):
|
||||
# Javascript only tests:
|
||||
def test_preview_and_save(self):
|
||||
self.get_url("wiki:edit", path="")
|
||||
self.fill(
|
||||
{
|
||||
"#id_content": "Some changed stuff",
|
||||
"#id_summary": "why edited",
|
||||
"#id_title": "wiki test",
|
||||
}
|
||||
)
|
||||
self.click("#id_preview")
|
||||
self.submit("#id_preview_save_changes")
|
||||
new_revision = URLPath.root().article.current_revision
|
||||
self.assertIn("Some changed stuff", new_revision.content)
|
||||
|
||||
|
||||
class SearchViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_query_string(self):
|
||||
response = self.client.get(
|
||||
resolve_url("wiki:search"), {"q": "Article"}
|
||||
)
|
||||
self.assertContains(response, "Root Article")
|
||||
|
||||
def test_empty_query_string(self):
|
||||
response = self.client.get(resolve_url("wiki:search"), {"q": ""})
|
||||
self.assertFalse(response.context["articles"])
|
||||
|
||||
def test_hierarchy_search(self):
|
||||
c = self.client
|
||||
|
||||
c.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Test0", "slug": "test0", "content": "Content test0"},
|
||||
)
|
||||
c.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Test1", "slug": "test1", "content": "Content test1"},
|
||||
)
|
||||
c.post(
|
||||
resolve_url("wiki:create", path="test0/"),
|
||||
{
|
||||
"title": "Subtest0",
|
||||
"slug": "subtest0",
|
||||
"content": "Content test2",
|
||||
},
|
||||
)
|
||||
|
||||
response = c.get(
|
||||
resolve_url("wiki:search", path="test0/"), {"q": "Content test"}
|
||||
)
|
||||
articles = response.context["articles"]
|
||||
|
||||
def contains_title(articles, title):
|
||||
return any(
|
||||
article.current_revision.title == title for article in articles
|
||||
)
|
||||
|
||||
self.assertIs(contains_title(articles, "Test0"), True)
|
||||
self.assertIs(contains_title(articles, "Test1"), False)
|
||||
self.assertIs(contains_title(articles, "Subtest0"), True)
|
||||
|
||||
def test_hierarchy_search_404(self):
|
||||
c = self.client
|
||||
|
||||
response = c.get(
|
||||
resolve_url("wiki:search", path="test0/"), {"q": "Content test"}
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
|
||||
class DeletedListViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_deleted_articles_list(self):
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Delete Me",
|
||||
"slug": "deleteme",
|
||||
"content": "delete me please!",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(
|
||||
response, resolve_url("wiki:get", path="deleteme/")
|
||||
)
|
||||
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:delete", path="deleteme/"),
|
||||
{
|
||||
"confirm": "on",
|
||||
"revision": URLPath.objects.get(
|
||||
slug="deleteme"
|
||||
).article.current_revision.id,
|
||||
},
|
||||
)
|
||||
|
||||
self.assertRedirects(response, resolve_url("wiki:get", path=""))
|
||||
|
||||
response = self.client.get(resolve_url("wiki:deleted_list"))
|
||||
self.assertContains(response, "Delete Me")
|
||||
|
||||
|
||||
class MergeViewTest(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_merge_preview(self):
|
||||
"""Test merge preview"""
|
||||
|
||||
first_revision = self.root_article.current_revision
|
||||
example_data = {
|
||||
"content": "More modifications\n\nMerge new line",
|
||||
"current_revision": str(first_revision.id),
|
||||
"preview": "0",
|
||||
"save": "1",
|
||||
"summary": "testing merge",
|
||||
"title": "wiki test",
|
||||
}
|
||||
|
||||
# save a new revision
|
||||
self.client.post(resolve_url("wiki:edit", path=""), example_data)
|
||||
|
||||
new_revision = models.Article.objects.get(
|
||||
id=self.root_article.id
|
||||
).current_revision
|
||||
|
||||
response = self.client.get(
|
||||
resolve_url(
|
||||
"wiki:merge_revision_preview",
|
||||
article_id=self.root_article.id,
|
||||
revision_id=first_revision.id,
|
||||
),
|
||||
)
|
||||
|
||||
self.assertContains(response, "Previewing merge between:")
|
||||
self.assertContains(
|
||||
response,
|
||||
f"#{first_revision.revision_number}",
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
f"#{new_revision.revision_number}",
|
||||
)
|
||||
|
||||
|
||||
class SourceViewTests(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_template_used(self):
|
||||
response = self.client.get(
|
||||
reverse("wiki:source", kwargs={"article_id": self.root_article.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, template_name="wiki/source.html")
|
||||
|
||||
def test_can_read_permission(self):
|
||||
# everybody can see the source of an article
|
||||
self.client.logout()
|
||||
response = self.client.get(
|
||||
reverse("wiki:source", kwargs={"article_id": self.root_article.pk})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_content(self):
|
||||
response = self.client.get(
|
||||
reverse("wiki:source", kwargs={"article_id": self.root_article.pk})
|
||||
)
|
||||
self.assertIn("Source of ", str(response.content))
|
||||
self.assertEqual(response.context["selected_tab"], "source")
|
||||
|
||||
|
||||
class HistoryViewTests(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_can_read_permission(self):
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wiki:history", kwargs={"article_id": self.root_article.pk}
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_content(self):
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wiki:history", kwargs={"article_id": self.root_article.pk}
|
||||
)
|
||||
)
|
||||
self.assertContains(response, "History:")
|
||||
self.assertEqual(response.context["selected_tab"], "history")
|
||||
|
||||
|
||||
class DirViewTests(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_browse_root(self):
|
||||
response = self.client.get(
|
||||
reverse("wiki:dir", kwargs={"path": ""}),
|
||||
)
|
||||
self.assertRegex(
|
||||
response.rendered_content,
|
||||
r'Browsing\s+<strong><a href=".+">/</a></strong>',
|
||||
)
|
||||
|
||||
def test_browse_root_query(self):
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{"title": "Test", "slug": "test0", "content": "Content .0."},
|
||||
)
|
||||
self.client.post(
|
||||
resolve_url("wiki:create", path="test0/"),
|
||||
{"title": "Test00", "slug": "test00", "content": "Content .00."},
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse("wiki:dir", kwargs={"path": ""}),
|
||||
{"query": "Test"},
|
||||
)
|
||||
self.assertRegex(response.rendered_content, r"1 article")
|
||||
response = self.client.get(
|
||||
reverse("wiki:dir", kwargs={"path": "test0/"}),
|
||||
{"query": "Test00"},
|
||||
)
|
||||
self.assertRegex(response.rendered_content, r"1 article")
|
||||
|
||||
|
||||
class SettingsViewTests(
|
||||
RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
|
||||
):
|
||||
def test_change_group(self):
|
||||
group = CustomGroup.objects.create()
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:settings", article_id=self.root_article.pk)
|
||||
+ "?f=form0",
|
||||
{"group": group.pk, "owner_username": SUPERUSER1_USERNAME},
|
||||
follow=True,
|
||||
)
|
||||
self.root_article.refresh_from_db()
|
||||
self.assertEqual(self.root_article.group, group)
|
||||
self.assertEqual(self.root_article.owner, self.superuser1)
|
||||
messages = list(get_messages(response.wsgi_request))
|
||||
self.assertEqual(len(messages), 1)
|
||||
message = messages[0]
|
||||
self.assertEqual(message.level, constants.SUCCESS)
|
||||
self.assertEqual(
|
||||
message.message,
|
||||
"Permission settings for the article were updated.",
|
||||
)
|
||||
|
||||
def test_change_invalid_owner(self):
|
||||
self.assertIsNone(self.root_article.owner)
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:settings", article_id=self.root_article.pk)
|
||||
+ "?f=form0",
|
||||
{"owner_username": "invalid"},
|
||||
follow=True,
|
||||
)
|
||||
self.assertEqual(
|
||||
response.context["forms"][0].errors["owner_username"],
|
||||
["No user with that username"],
|
||||
)
|
||||
|
||||
def test_unchanged_message(self):
|
||||
# 1. This is not pretty: Constructs a request object to use to construct
|
||||
# the PermissionForm
|
||||
get_response = self.client.get(
|
||||
resolve_url("wiki:settings", article_id=self.root_article.pk)
|
||||
)
|
||||
# 2. Construct a PermissionForm
|
||||
form = PermissionsForm(self.root_article, get_response.wsgi_request)
|
||||
# 3. ...in order to get the POST form values that will be transmitted
|
||||
form_values = {field.html_name: field.value() or "" for field in form}
|
||||
# 4. Send an unchanged form
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:settings", article_id=self.root_article.pk)
|
||||
+ "?f=form0",
|
||||
form_values,
|
||||
follow=True,
|
||||
)
|
||||
messages = list(get_messages(response.wsgi_request))
|
||||
self.assertEqual(len(messages), 1)
|
||||
message = messages[0]
|
||||
self.assertEqual(message.level, constants.SUCCESS)
|
||||
self.assertEqual(
|
||||
message.message,
|
||||
"Your permission settings were unchanged, so nothing saved.",
|
||||
)
|
||||
|
||||
@override_settings(ACCOUNT_HANDLING=True)
|
||||
def test_login_required(self):
|
||||
self.client.logout()
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wiki:settings", kwargs={"article_id": self.root_article.pk}
|
||||
)
|
||||
)
|
||||
# it's redirecting
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_auth_user(self):
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wiki:settings", kwargs={"article_id": self.root_article.pk}
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_normal_user(self):
|
||||
"""
|
||||
Tests that the settings view page renders for a normal user
|
||||
Regression test: https://github.com/django-wiki/django-wiki/issues/1058
|
||||
"""
|
||||
response = self.client.post(
|
||||
resolve_url("wiki:create", path=""),
|
||||
{
|
||||
"title": "Level 1",
|
||||
"slug": "Level1",
|
||||
"content": "Content level 1",
|
||||
},
|
||||
)
|
||||
self.client.login(
|
||||
username=NORMALUSER1_USERNAME, password=NORMALUSER1_PASSWORD
|
||||
)
|
||||
response = self.client.get(
|
||||
reverse("wiki:settings", kwargs={"path": "level1/"})
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_content(self):
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wiki:settings", kwargs={"article_id": self.root_article.pk}
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.context["selected_tab"], "settings")
|
||||
Reference in New Issue
Block a user