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")
