#
# Copyright © 2012 - 2021 Michal Čihař <michal@cihar.com>
#
# This file is part of Weblate <https://weblate.org/>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#

"""Test for translation views."""

import time
from unittest import SkipTest

from django.urls import reverse

from weblate.trans.models import Change, Component, Unit
from weblate.trans.tests.test_views import ViewTestCase
from weblate.trans.util import join_plural
from weblate.utils.hash import hash_to_checksum
from weblate.utils.state import STATE_FUZZY, STATE_READONLY, STATE_TRANSLATED


class EditTest(ViewTestCase):
    """Test for manipulating translation."""

    has_plurals = True
    source = "Hello, world!\n"
    target = "Nazdar svete!\n"
    second_target = "Ahoj svete!\n"
    already_translated = 0
    needs_bilingual_context = False

    def setUp(self):
        super().setUp()
        self.translation = self.get_translation()
        self.translate_url = reverse("translate", kwargs=self.kw_translation)

    def test_edit(self):
        response = self.edit_unit(self.source, self.target)
        # We should get to second message
        self.assert_redirects_offset(response, self.translate_url, 2)
        unit = self.get_unit(source=self.source)
        self.assertEqual(unit.target, self.target)
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assert_backend(self.already_translated + 1)

        # Test that second edit with no change does not break anything
        response = self.edit_unit(self.source, self.target)
        # We should get to second message
        self.assert_redirects_offset(response, self.translate_url, 2)
        unit = self.get_unit(source=self.source)
        self.assertEqual(unit.target, self.target)
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assert_backend(self.already_translated + 1)

        # Test that third edit still works
        response = self.edit_unit(self.source, self.second_target)
        # We should get to second message
        self.assert_redirects_offset(response, self.translate_url, 2)
        unit = self.get_unit(source=self.source)
        self.assertEqual(unit.target, self.second_target)
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assert_backend(self.already_translated + 1)

    def test_plurals(self):
        """Test plural editing."""
        if not self.has_plurals:
            return

        response = self.edit_unit(
            "Orangutan",
            "Opice má %d banán.\n",
            target_1="Opice má %d banány.\n",
            target_2="Opice má %d banánů.\n",
        )
        # We should get to next message
        self.assert_redirects_offset(response, self.translate_url, 3)
        # Check translations
        unit = self.get_unit("Orangutan")
        plurals = unit.get_target_plurals()
        self.assertEqual(len(plurals), 3)
        self.assertEqual(plurals[0], "Opice má %d banán.\n")
        self.assertEqual(plurals[1], "Opice má %d banány.\n")
        self.assertEqual(plurals[2], "Opice má %d banánů.\n")

    def test_fuzzy(self):
        """Test for fuzzy flag handling."""
        unit = self.get_unit(source=self.source)
        self.assertNotEqual(unit.state, STATE_FUZZY)
        self.edit_unit(self.source, self.target, fuzzy="yes", review="10")
        unit = self.get_unit(source=self.source)
        self.assertEqual(unit.state, STATE_FUZZY)
        self.assertEqual(unit.target, self.target)
        self.assertFalse(unit.has_failing_check)
        self.edit_unit(self.source, self.target)
        unit = self.get_unit(source=self.source)
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assertEqual(unit.target, self.target)
        self.assertFalse(unit.has_failing_check)
        self.edit_unit(self.source, self.target, fuzzy="yes")
        unit = self.get_unit(source=self.source)
        self.assertEqual(unit.state, STATE_FUZZY)
        self.assertEqual(unit.target, self.target)
        # Should have was translated check
        self.assertTrue(unit.has_failing_check)

    def add_unit(self, key, force_source: bool = False):
        if force_source or self.component.has_template():
            args = {"context": key, "source_0": "Source string" * 100000}
            language = "en"
        else:
            args = {"source_0": key, "target_0": "Translation string"}
            if self.needs_bilingual_context:
                args["context"] = key * 2
            language = "cs"
        return self.client.post(
            reverse(
                "new-unit",
                kwargs={
                    "project": self.component.project.slug,
                    "component": self.component.slug,
                    "lang": language,
                },
            ),
            args,
            follow=True,
        )

    def test_new_unit(self):
        # No permissions
        response = self.add_unit("key")
        self.assertEqual(response.status_code, 403)

        self.make_manager()

        # No adding
        self.component.manage_units = False
        self.component.save()
        response = self.add_unit("key")
        self.assertEqual(response.status_code, 403)

        # Adding allowed (if format supports that)
        self.component.manage_units = True
        self.component.save()
        response = self.add_unit("key")
        if not self.component.file_format_cls.can_add_unit:
            self.assertEqual(response.status_code, 403)
            return
        self.assertContains(response, "New string has been added")

        # Duplicate string
        response = self.add_unit("key")
        self.assertContains(response, "This string seems to already exist.")

        # Invalid params
        response = self.add_unit("")
        self.assertContains(response, "Error in parameter ")

        # Adding on source in bilingual
        if (
            not self.component.has_template()
            and self.component.file_format_cls.can_add_unit
        ):
            start = Unit.objects.count()
            response = self.add_unit("Test string", force_source=True)
            self.assertContains(response, "New string has been added")
            self.assertEqual(
                start + self.component.translation_set.count(),
                Unit.objects.count(),
            )

        # Make sure writing out pending units works
        self.component.commit_pending("test", None)


class EditValidationTest(ViewTestCase):
    def edit(self, **kwargs):
        """Editing with no specific params."""
        unit = self.get_unit()
        params = {"checksum": unit.checksum}
        params.update(kwargs)
        return self.client.post(
            unit.translation.get_translate_url(), params, follow=True
        )

    def test_edit_invalid(self):
        """Editing with invalid params."""
        response = self.edit()
        self.assertContains(response, "Missing translated string!")

    def test_suggest_invalid(self):
        """Suggesting with invalid params."""
        response = self.edit(suggest="1")
        self.assertContains(response, "Missing translated string!")

    def test_merge(self):
        """Merging with invalid parameter."""
        unit = self.get_unit()
        response = self.client.post(
            unit.translation.get_translate_url() + "?checksum=" + unit.checksum,
            {"merge": "invalid"},
            follow=True,
        )
        self.assertContains(response, "Invalid merge request!")

    def test_merge_lang(self):
        """Merging across languages."""
        unit = self.get_unit()
        trans = self.component.translation_set.exclude(language_code="cs")[0]
        other = trans.unit_set.get(source=unit.source, context=unit.context)
        response = self.client.post(
            unit.translation.get_translate_url() + "?checksum=" + unit.checksum,
            {"merge": other.pk},
            follow=True,
        )
        self.assertContains(response, "Invalid merge request!")

    def test_revert(self):
        unit = self.get_unit()
        # Try the merge
        response = self.client.get(
            unit.translation.get_translate_url(),
            {"checksum": unit.checksum, "revert": "invalid"},
            follow=True,
        )
        self.assertContains(response, "Invalid revert request!")
        # Try the merge
        response = self.client.get(
            unit.translation.get_translate_url(),
            {"checksum": unit.checksum, "revert": -1},
            follow=True,
        )
        self.assertContains(response, "Invalid revert request!")


class EditResourceTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_android()


class EditLanguageTest(EditTest):
    """Language wide editing tests."""

    def setUp(self):
        super().setUp()
        self.translate_url = reverse(
            "translate",
            kwargs={"project": self.project.slug, "lang": "cs", "component": "-"},
        )

    def edit_unit(self, source, target, language="cs", **kwargs):
        """Do edit single unit using web interface."""
        unit = self.get_unit(source, language)
        params = {
            "checksum": unit.checksum,
            "contentsum": hash_to_checksum(unit.content_hash),
            "translationsum": hash_to_checksum(unit.get_target_hash()),
            "target_0": target,
            "review": "20",
        }
        params.update(kwargs)
        return self.client.post(self.translate_url, params)


class EditResourceSourceTest(ViewTestCase):
    """Source strings (template) editing."""

    has_plurals = False

    def test_edit(self):
        translate_url = reverse(
            "translate", kwargs={"project": "test", "component": "test", "lang": "en"}
        )

        response = self.edit_unit("Hello, world!\n", "Nazdar svete!\n", "en")
        # We should get to second message
        self.assert_redirects_offset(response, translate_url, 2)
        unit = self.get_unit("Nazdar svete!\n", "en")
        self.assertEqual(unit.target, "Nazdar svete!\n")
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assert_backend(4, "en")

    def test_edit_revert(self):
        translation = self.get_translation()
        # Edit translation
        self.edit_unit("Hello, world!\n", "Nazdar svete!\n", "cs")

        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_TRANSLATED)

        # Edit source
        self.edit_unit("Hello, world!\n", "Hello, universe!\n", "en")

        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_FUZZY)

        # Revert source
        self.edit_unit("Hello, universe!\n", "Hello, world!\n", "en")

        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_TRANSLATED)

    def test_needs_edit(self):
        translation = self.get_translation()

        # Edit translation
        self.edit_unit("Hello, world!\n", "Nazdar svete!\n", "cs")

        # Change state
        self.edit_unit("Hello, world!\n", "Hello, world!\n", "en", fuzzy="yes")
        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_READONLY)

        # Change state and source
        self.edit_unit("Hello, world!\n", "Hello, universe!\n", "en", fuzzy="yes")
        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_READONLY)

        # Change state and source
        self.edit_unit("Hello, universe!\n", "Hello, universe!\n", "en")
        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_FUZZY)

        # Revert source
        self.edit_unit("Hello, universe!\n", "Hello, world!\n", "en")
        unit = translation.unit_set.get(context="hello")
        self.assertEqual(unit.state, STATE_TRANSLATED)

    def create_component(self):
        return self.create_android()


class EditBranchTest(EditTest):
    def create_component(self):
        return self.create_po_branch()


class EditMercurialTest(EditTest):
    def create_component(self):
        return self.create_po_mercurial()


class EditPoMonoTest(EditTest):
    def create_component(self):
        return self.create_po_mono()

    def test_remove_unit(self):
        self.assertEqual(self.component.stats.all, 16)
        unit = self.get_unit()
        # Deleting translation unit
        response = self.client.post(reverse("delete-unit", kwargs={"unit_id": unit.pk}))
        self.assertEqual(response.status_code, 403)
        # Lack of permissions
        response = self.client.post(
            reverse("delete-unit", kwargs={"unit_id": unit.source_unit.pk})
        )
        self.assertEqual(response.status_code, 403)
        # Make superuser
        self.user.is_superuser = True
        self.user.save()
        # Deleting translation unit
        response = self.client.post(reverse("delete-unit", kwargs={"unit_id": unit.pk}))
        self.assertEqual(response.status_code, 403)
        # Actual removal
        response = self.client.post(
            reverse("delete-unit", kwargs={"unit_id": unit.source_unit.pk})
        )
        self.assertEqual(response.status_code, 302)
        component = Component.objects.get(pk=self.component.pk)
        self.assertEqual(component.stats.all, 12)


class EditIphoneTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_iphone()

    def test_new_unit(self):
        # Most likely the test is wrong here it is using monolingual format as bilingual
        # and duplicates source into context
        raise SkipTest("Not supported")


class EditJSONTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_json()


class EditJoomlaTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_joomla()


class EditRubyYAMLTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_ruby_yaml()


class EditDTDTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_dtd()

    def test_new_unit(self):
        # Most likely there is a bug in the format and adding is broken
        raise SkipTest("Not supported")


class EditJSONMonoTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_json_mono()


class EditJavaTest(EditTest):
    has_plurals = False
    already_translated = 1

    def create_component(self):
        return self.create_java()


class EditAppStoreTest(EditTest):
    has_plurals = False
    source = "Weblate - continuous localization"
    target = "Weblate - průběžná lokalizace"
    second_target = "Weblate - průběžný překlad"
    already_translated = 2

    def create_component(self):
        return self.create_appstore()


class EditXliffComplexTest(EditTest):
    has_plurals = False
    needs_bilingual_context = True

    def create_component(self):
        return self.create_xliff("complex")

    def test_invalid_xml(self):
        self.edit_unit("Hello, world!\n", "Nazdar & svete!\n")
        self.assert_backend(1)

    def test_new_unit(self):
        # The group handling is broken, see
        # https://github.com/translate/translate/issues/4186
        raise SkipTest("Not supported")


class EditXliffResnameTest(EditTest):
    has_plurals = False
    needs_bilingual_context = True

    def create_component(self):
        return self.create_xliff("only-resname")


class EditXliffTest(EditTest):
    has_plurals = False
    needs_bilingual_context = True

    def create_component(self):
        return self.create_xliff()


class EditXliffMonoTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_xliff_mono()

    def test_new_unit(self):
        # The group handling is broken, see
        # https://github.com/translate/translate/issues/4186
        raise SkipTest("Not supported")


class EditLinkTest(EditTest):
    def create_component(self):
        return self.create_link()


class EditTSTest(EditTest):
    def create_component(self):
        return self.create_ts()


class EditTSMonoTest(EditTest):
    has_plurals = False

    def create_component(self):
        return self.create_ts_mono()


class ZenViewTest(ViewTestCase):
    def test_zen(self):
        response = self.client.get(reverse("zen", kwargs=self.kw_translation))
        self.assertContains(response, "Thank you for using Weblate.")
        self.assertContains(response, "Orangutan has %d bananas")
        self.assertContains(response, "The translation has come to an end.")

    def test_zen_invalid(self):
        response = self.client.get(
            reverse("zen", kwargs=self.kw_translation),
            {"q": "has:nonexisting"},
            follow=True,
        )
        self.assertContains(response, "Unsupported has lookup")

    def test_load_zen(self):
        response = self.client.get(reverse("load_zen", kwargs=self.kw_translation))
        self.assertContains(response, "Thank you for using Weblate.")
        self.assertContains(response, "Orangutan has %d bananas")
        self.assertContains(response, "The translation has come to an end.")

    def test_load_zen_offset(self):
        response = self.client.get(
            reverse("load_zen", kwargs=self.kw_translation), {"offset": "2"}
        )
        self.assertNotContains(response, "Hello, world")
        self.assertContains(response, "Orangutan has %d bananas")
        response = self.client.get(
            reverse("load_zen", kwargs=self.kw_translation), {"offset": "bug"}
        )
        self.assertContains(response, "Hello, world")

    def test_save_zen(self):
        unit = self.get_unit()
        params = {
            "checksum": unit.checksum,
            "contentsum": hash_to_checksum(unit.content_hash),
            "translationsum": hash_to_checksum(unit.get_target_hash()),
            "target_0": "Zen translation",
            "review": "20",
        }
        response = self.client.post(
            reverse("save_zen", kwargs=self.kw_translation), params
        )
        self.assertContains(
            response,
            "Following fixups were applied to translation: "
            "Trailing and leading whitespace",
        )

    def test_save_zen_lock(self):
        self.component.locked = True
        self.component.save()
        unit = self.get_unit()
        params = {
            "checksum": unit.checksum,
            "contentsum": hash_to_checksum(unit.content_hash),
            "translationsum": hash_to_checksum(unit.get_target_hash()),
            "target_0": "Zen translation",
            "review": "20",
        }
        response = self.client.post(
            reverse("save_zen", kwargs=self.kw_translation), params
        )
        self.assertContains(
            response, "Insufficient privileges for saving translations."
        )

    def test_browse(self):
        response = self.client.get(reverse("browse", kwargs=self.kw_translation))
        self.assertContains(response, "Thank you for using Weblate.")
        self.assertContains(response, "Orangutan has %d banana")


class EditComplexTest(ViewTestCase):
    """Test for complex manipulating translation."""

    def setUp(self):
        super().setUp()
        self.translation = self.get_translation()
        self.translate_url = reverse("translate", kwargs=self.kw_translation)

    def test_merge(self):
        # Translate unit to have something to start with
        response = self.edit_unit("Hello, world!\n", "Nazdar svete!\n")
        unit = self.get_unit()
        # Try the merge
        response = self.client.post(
            self.translate_url + "?checksum=" + unit.checksum, {"merge": unit.id}
        )
        self.assert_backend(1)
        # We should stay on same message
        self.assert_redirects_offset(response, self.translate_url, unit.position + 1)

        # Test error handling
        unit2 = self.translation.unit_set.get(source="Thank you for using Weblate.")
        response = self.client.post(
            self.translate_url + "?checksum=" + unit.checksum, {"merge": unit2.id}
        )
        self.assertContains(response, "Invalid merge request!")

    def test_merge_inconsistent(self):
        # Translate unit to have something to start with
        self.edit_unit("Hello, world!\n", "Nazdar svete!\n")
        units = Unit.objects.filter(
            translation__language__code="cs", source="Hello, world!\n"
        )
        self.assertEqual(
            set(units.values_list("target", flat=True)), {"Nazdar svete!\n"}
        )
        self.create_link_existing()
        self.assertEqual(
            set(units.values_list("target", flat=True)), {"Nazdar svete!\n", ""}
        )
        unit = self.get_unit()
        self.assertEqual(unit.all_checks_names, {"inconsistent"})
        self.client.post(
            self.translate_url + "?checksum=" + unit.checksum, {"merge": unit.id}
        )
        self.assertEqual(
            set(units.values_list("target", flat=True)), {"Nazdar svete!\n"}
        )
        unit = self.get_unit()
        self.assertEqual(unit.all_checks_names, set())

    def test_edit_propagated(self):
        units = Unit.objects.filter(
            translation__language__code="cs", source="Thank you for using Weblate."
        )
        self.create_link_existing()
        self.assertEqual(set(units.values_list("target", flat=True)), {""})
        self.edit_unit("Thank you for using Weblate.", "Díky za použití Weblate")
        self.assertEqual(
            set(units.values_list("target", flat=True)), {"Díky za použití Weblate"}
        )
        self.assertEqual(
            [unit.all_checks_names for unit in units.iterator()],
            [{"end_stop"}, {"end_stop"}],
        )
        self.edit_unit("Thank you for using Weblate.", "Díky za použití Weblate.")
        self.assertEqual(
            set(units.values_list("target", flat=True)), {"Díky za použití Weblate."}
        )
        self.assertEqual(
            [unit.all_checks_names for unit in units.iterator()], [set(), set()]
        )

    def test_revert(self):
        source = "Hello, world!\n"
        target = "Nazdar svete!\n"
        target_2 = "Hei maailma!\n"
        self.edit_unit(source, target)
        # Ensure other edit gets different timestamp
        time.sleep(1)
        self.edit_unit(source, target_2)
        unit = self.get_unit()
        changes = Change.objects.content().filter(unit=unit).order()
        self.assertEqual(changes[1].target, target)
        self.assertEqual(changes[0].target, target_2)
        self.assert_backend(1)
        # revert it
        self.client.get(
            self.translate_url, {"checksum": unit.checksum, "revert": changes[1].id}
        )
        unit = self.get_unit()
        self.assertEqual(unit.target, target_2)
        # check that we cannot revert to string from another translation
        self.edit_unit("Thank you for using Weblate.", "Kiitoksia Weblaten kaytosta.")
        unit2 = self.get_unit(source="Thank you for using Weblate.")
        change = unit2.change_set.order()[0]
        response = self.client.get(
            self.translate_url, {"checksum": unit.checksum, "revert": change.id}
        )
        self.assertContains(response, "Invalid revert request!")
        self.assert_backend(2)

    def test_revert_plural(self):
        source = "Orangutan has %d banana.\n"
        target = [
            "Opice má %d banán.\n",
            "Opice má %d banány.\n",
            "Opice má %d banánů.\n",
        ]
        target_2 = [
            "Orangutan má %d banán.\n",
            "Orangutan má %d banány.\n",
            "Orangutan má %d banánů.\n",
        ]
        self.edit_unit(source, target[0], target_1=target[1], target_2=target[2])
        # Ensure other edit gets different timestamp
        time.sleep(1)
        self.edit_unit(source, target_2[0], target_1=target_2[1], target_2=target_2[2])
        unit = self.get_unit(source)
        changes = Change.objects.content().filter(unit=unit).order()
        self.assertEqual(changes[1].target, join_plural(target))
        self.assertEqual(changes[0].target, join_plural(target_2))
        self.assert_backend(1)
        # revert it
        self.client.get(
            self.translate_url, {"checksum": unit.checksum, "revert": changes[0].id}
        )
        unit = self.get_unit(source)
        self.assertEqual(unit.get_target_plurals(), target)

    def test_edit_fixup(self):
        # Save with failing check
        response = self.edit_unit("Hello, world!\n", "Nazdar svete!")
        # We should get to second message
        self.assert_redirects_offset(response, self.translate_url, 2)
        unit = self.get_unit()
        self.assertEqual(unit.target, "Nazdar svete!\n")
        self.assertFalse(unit.has_failing_check)
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(len(unit.active_checks), 0)
        self.assertEqual(unit.translation.stats.allchecks, 0)
        self.assert_backend(1)

    def test_edit_check(self):
        # Save with failing check
        response = self.edit_unit("Hello, world!\n", "Hello, world!\n")
        # We should stay on current message
        self.assert_redirects_offset(response, self.translate_url, 1)
        unit = self.get_unit()
        self.assertEqual(unit.target, "Hello, world!\n")
        self.assertTrue(unit.has_failing_check)
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assertEqual(len(unit.all_checks), 1)
        self.assertEqual(len(unit.active_checks), 1)
        self.assertEqual(unit.translation.stats.allchecks, 1)

        # Ignore check
        check_id = unit.active_checks[0].id
        response = self.client.post(
            reverse("js-ignore-check", kwargs={"check_id": check_id})
        )
        self.assertContains(response, "ok")
        # Should have one less failing check
        unit = self.get_unit()
        self.assertFalse(unit.has_failing_check)
        self.assertEqual(len(unit.all_checks), 1)
        self.assertEqual(len(unit.active_checks), 0)
        self.assertEqual(unit.translation.stats.allchecks, 0)
        # Ignore check for all languages
        ignore_url = reverse("js-ignore-check-source", kwargs={"check_id": check_id})
        response = self.client.post(ignore_url)
        self.assertEqual(response.status_code, 403)
        self.user.is_superuser = True
        self.user.save()
        response = self.client.post(ignore_url)
        self.assertContains(response, "ok")
        # Should have one less check
        unit = self.get_unit()
        self.assertFalse(unit.has_failing_check)
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(len(unit.active_checks), 0)
        self.assertEqual(unit.translation.stats.allchecks, 0)

        # Save with no failing checks
        response = self.edit_unit("Hello, world!\n", "Nazdar svete!\n")
        # We should stay on current message
        self.assert_redirects_offset(response, self.translate_url, 2)
        unit = self.get_unit()
        self.assertEqual(unit.target, "Nazdar svete!\n")
        self.assertFalse(unit.has_failing_check)
        self.assertEqual(len(unit.all_checks), 0)
        self.assertEqual(unit.translation.stats.allchecks, 0)
        self.assert_backend(1)

    def test_enforced_check(self):
        # Enforce same check
        self.component.enforced_checks = ["same"]
        self.component.save(update_fields=["enforced_checks"])
        # Save with failing check
        response = self.edit_unit("Hello, world!\n", "Hello, world!\n")
        # We should stay on current message
        self.assert_redirects_offset(response, self.translate_url, 1)
        unit = self.get_unit()
        self.assertEqual(unit.target, "Hello, world!\n")
        self.assertEqual(unit.state, STATE_FUZZY)
        self.assertTrue(unit.has_failing_check)
        self.assertEqual(len(unit.all_checks), 1)
        self.assertEqual(len(unit.active_checks), 1)
        self.assertEqual(unit.translation.stats.allchecks, 1)

    def test_commit_push(self):
        response = self.edit_unit("Hello, world!\n", "Nazdar svete!\n")
        # We should get to second message
        self.assert_redirects_offset(response, self.translate_url, 2)
        self.assertTrue(self.translation.needs_commit())
        self.assertTrue(self.component.needs_commit())
        self.assertTrue(self.component.project.needs_commit())

        self.translation.commit_pending("test", self.user)

        self.assertFalse(self.translation.needs_commit())
        self.assertFalse(self.component.needs_commit())
        self.assertFalse(self.component.project.needs_commit())

        self.assertTrue(self.translation.repo_needs_push())
        self.assertTrue(self.component.repo_needs_push())
        self.assertTrue(self.component.project.repo_needs_push())

        self.translation.do_push(self.get_request())

        self.assertFalse(self.translation.repo_needs_push())
        self.assertFalse(self.component.repo_needs_push())
        self.assertFalse(self.component.project.repo_needs_push())

    def test_edit_locked(self):
        self.component.locked = True
        self.component.save()
        response = self.edit_unit("Hello, world!\n", "Nazdar svete!\n")
        # We should get to second message
        self.assertContains(
            response,
            "The translation is temporarily closed for contributions due "
            "to maintenance, please come back later.",
        )
        self.assert_backend(0)

    def test_edit_changed_source(self):
        # We use invalid contentsum here
        response = self.edit_unit(
            "Hello, world!\n", "Nazdar svete!\n", contentsum="aaa"
        )
        # We should get an error message
        self.assertContains(response, "Source string has been changed meanwhile")
        self.assert_backend(0)

    def test_edit_changed_translation(self):
        # We use invalid translationsum here
        response = self.edit_unit(
            "Hello, world!\n", "Nazdar svete!\n", translationsum="aaa"
        )
        # We should get an error message
        self.assertContains(
            response, "Translation of the string has been changed meanwhile"
        )
        self.assert_backend(0)

    def test_edit_view(self):
        url = self.get_unit("Hello, world!\n").get_absolute_url()
        response = self.client.get(url)
        form = response.context["form"]
        params = {}
        for field in form.fields.keys():
            params[field] = form[field].value()
        params["target_0"] = "Nazdar svete!\n"
        response = self.client.post(url, params)
        unit = self.get_unit()
        self.assertEqual(unit.target, "Nazdar svete!\n")
        self.assertEqual(unit.state, STATE_TRANSLATED)
        self.assert_backend(1)
