rafalp 6 лет назад
Родитель
Сommit
5e25cb5055

+ 7 - 9
misago/themes/admin/forms.py

@@ -7,7 +7,7 @@ from ..models import Theme, Css
 from .css import create_css, get_next_css_order
 from .media import create_media
 from .utils import get_file_hash
-from .validators import validate_css_name
+from .validators import validate_css_name, validate_css_name_is_available
 
 
 class ThemeChoiceField(TreeNodeChoiceField):
@@ -129,14 +129,7 @@ class CssEditorForm(forms.ModelForm):
 
     def clean_name(self):
         data = self.cleaned_data["name"]
-        queryset = self.instance.theme.css.filter(name=data)
-        if self.instance.pk:
-            queryset = queryset.exclude(pk=self.instance.pk)
-        if queryset.exists():
-            raise forms.ValidationError(
-                gettext("This name is already in use by other asset.")
-            )
-
+        validate_css_name_is_available(self.instance, data)
         return data
 
     def clean(self):
@@ -177,6 +170,11 @@ class CssLinkForm(forms.ModelForm):
         model = Css
         fields = ["name", "url"]
 
+    def clean_name(self):
+        data = self.cleaned_data["name"]
+        validate_css_name_is_available(self.instance, data)
+        return data
+
     def save(self):
         if not self.instance.pk:
             self.instance.order = get_next_css_order(self.instance.theme)

+ 1 - 1
misago/themes/admin/tests/conftest.py

@@ -40,7 +40,7 @@ def css(admin_client, theme):
 @pytest.fixture
 def css_link(admin_client, theme):
     return theme.css.create(
-        name="CSS link", url="https://test.cdn/somefont.css", order=theme.css.count()
+        name="CSS link", url="https://example.com/cdn.css", order=theme.css.count()
     )
 
 

+ 8 - 2
misago/themes/admin/tests/test_browsing_theme_assets.py

@@ -18,11 +18,16 @@ def test_theme_assets_list_is_displayed(assets_client, theme):
     assert_contains(response, theme.name)
 
 
-def test_css_is_displayed_on_theme_asset_list(assets_client, theme, css):
+def test_css_file_is_displayed_on_theme_asset_list(assets_client, theme, css):
     response = assets_client(theme)
     assert_contains(response, css.name)
 
 
+def test_css_link_is_displayed_on_theme_asset_list(assets_client, theme, css_link):
+    response = assets_client(theme)
+    assert_contains(response, css_link.name)
+
+
 def test_media_is_displayed_on_themes_asset_list(assets_client, theme, media):
     response = assets_client(theme)
     assert_contains(response, media.name)
@@ -39,10 +44,11 @@ def test_image_thumbnail_is_displayed_on_themes_asset_list(assets_client, theme,
 
 
 def test_other_theme_assets_are_not_displayed(
-    assets_client, other_theme, css, media, image
+    assets_client, other_theme, css, css_link, media, image
 ):
     response = assets_client(other_theme)
     assert_not_contains(response, css.name)
+    assert_not_contains(response, css_link.name)
     assert_not_contains(response, media.name)
     assert_not_contains(response, image.name)
 

+ 27 - 6
misago/themes/admin/tests/test_css_creation_and_edition.py → misago/themes/admin/tests/test_css_files_creation_and_edition.py

@@ -23,7 +23,7 @@ def edit_link(theme, css):
 
 @pytest.fixture
 def data():
-    return {"name": "test.css", "source": (".page-header { padding: 0}")}
+    return {"name": "test.css", "source": ".page-header { padding: 0}"}
 
 
 def test_css_creation_form_is_displayed(admin_client, create_link):
@@ -124,6 +124,14 @@ def test_css_creation_fails_if_source_is_not_given(
     assert not theme.css.exists()
 
 
+def test_css_file_is_created_with_correct_order(
+    theme, admin_client, create_link, css_link, data
+):
+    admin_client.post(create_link, data)
+    css = theme.css.get(name=data["name"])
+    assert css.order == 1
+
+
 def test_error_message_is_set_if_user_attempts_to_create_css_in_default_theme(
     default_theme, admin_client
 ):
@@ -167,7 +175,7 @@ def test_css_edition_form_contains_source_file_contents(admin_client, edit_link,
     assert_contains(response, css.source_file.read().decode("utf-8"))
 
 
-def test_name_can_be_changed(admin_client, edit_link, css, data):
+def test_css_name_can_be_changed(admin_client, edit_link, css, data):
     data["name"] = "new-name.css"
     admin_client.post(edit_link, data)
 
@@ -175,7 +183,9 @@ def test_name_can_be_changed(admin_client, edit_link, css, data):
     assert css.name == data["name"]
 
 
-def test_name_change_also_changes_source_file_name(admin_client, edit_link, css, data):
+def test_css_name_change_also_changes_source_file_name(
+    admin_client, edit_link, css, data
+):
     data["name"] = "new-name.css"
     admin_client.post(edit_link, data)
 
@@ -235,7 +245,7 @@ def test_file_is_not_updated_if_form_data_has_no_changes(
     assert original_mtime == os.path.getmtime(css.source_file.path)
 
 
-def test_file_order_stays_the_same_after_edit(admin_client, edit_link, css, data):
+def test_css_order_stays_the_same_after_edit(admin_client, edit_link, css, data):
     original_order = css.order
     data["name"] = "changed.css"
     admin_client.post(edit_link, data)
@@ -252,7 +262,7 @@ def test_css_edit_form_redirects_user_to_edition_after_saving(
     assert response["location"] == edit_link
 
 
-def test_error_message_is_set_if_user_attempts_to_edit_css_in_default_theme(
+def test_error_message_is_set_if_user_attempts_to_edit_css_file_in_default_theme(
     default_theme, admin_client
 ):
     edit_link = reverse(
@@ -263,7 +273,7 @@ def test_error_message_is_set_if_user_attempts_to_edit_css_in_default_theme(
     assert_has_error_message(response)
 
 
-def test_error_message_is_set_if_user_attempts_to_edit_css_in_nonexisting_theme(
+def test_error_message_is_set_if_user_attempts_to_edit_css_file_in_nonexisting_theme(
     nonexisting_theme, admin_client
 ):
     edit_link = reverse(
@@ -294,3 +304,14 @@ def test_error_message_is_set_if_user_attempts_to_edit_nonexisting_css(
     )
     response = admin_client.get(edit_link)
     assert_has_error_message(response)
+
+
+def test_error_message_is_set_if_user_attempts_to_edit_css_link_with_file_form(
+    theme, admin_client, css_link
+):
+    edit_link = reverse(
+        "misago:admin:appearance:themes:edit-css-file",
+        kwargs={"pk": theme.pk, "css_pk": css_link.pk},
+    )
+    response = admin_client.get(edit_link)
+    assert_has_error_message(response)

+ 204 - 0
misago/themes/admin/tests/test_css_links_creation_and_deletion.py

@@ -0,0 +1,204 @@
+import pytest
+from django.urls import reverse
+
+from ....test import assert_contains, assert_has_error_message
+
+
+@pytest.fixture
+def create_link(theme):
+    return reverse(
+        "misago:admin:appearance:themes:new-css-link", kwargs={"pk": theme.pk}
+    )
+
+
+@pytest.fixture
+def edit_link(theme, css_link):
+    return reverse(
+        "misago:admin:appearance:themes:edit-css-link",
+        kwargs={"pk": theme.pk, "css_pk": css_link.pk},
+    )
+
+
+@pytest.fixture
+def data():
+    return {"name": "CSS link", "url": "https://example.com/cdn.css"}
+
+
+@pytest.fixture(autouse=True)
+def mock_task(mocker):
+    delay = mocker.Mock()
+    mocker.patch(
+        "misago.themes.admin.views.update_remote_css_size", mocker.Mock(delay=delay)
+    )
+    return delay
+
+
+def test_css_link_creation_form_is_displayed(admin_client, create_link):
+    response = admin_client.get(create_link)
+    assert response.status_code == 200
+    assert_contains(response, "New CSS link")
+
+
+def test_css_link_can_be_created(theme, admin_client, create_link, data):
+    admin_client.post(create_link, data)
+    assert theme.css.exists()
+
+
+def test_css_link_is_created_with_entered_name(theme, admin_client, create_link, data):
+    admin_client.post(create_link, data)
+    assert theme.css.last().name == data["name"]
+
+
+def test_css_link_name_can_be_descriptive(theme, admin_client, create_link, data):
+    data["name"] = "font (from font.hosting.com)"
+    admin_client.post(create_link, data)
+    assert theme.css.last().name == data["name"]
+
+
+def test_css_link_creation_fails_if_name_is_not_given(
+    theme, admin_client, create_link, data
+):
+    data["name"] = ""
+    admin_client.post(create_link, data)
+    assert not theme.css.exists()
+
+
+def test_css_link_creation_fails_if_name_is_already_taken_by_other_css_in_theme(
+    theme, admin_client, create_link, data, css
+):
+    data["name"] = css.name
+    admin_client.post(create_link, data)
+    assert theme.css.count() == 1
+
+
+def test_css_link_name_usage_check_passess_if_name_is_used_by_other_theme_css(
+    other_theme, admin_client, data, css
+):
+    create_link = reverse(
+        "misago:admin:appearance:themes:new-css-link", kwargs={"pk": other_theme.pk}
+    )
+    data["name"] = css.name
+    admin_client.post(create_link, data)
+    assert other_theme.css.exists()
+
+
+def test_css_link_creation_fails_if_url_is_not_given(
+    theme, admin_client, create_link, data
+):
+    data["url"] = ""
+    admin_client.post(create_link, data)
+    assert not theme.css.exists()
+
+
+def test_css_link_creation_fails_if_url_is_not_valid(
+    theme, admin_client, create_link, data
+):
+    data["url"] = "invalid-url"
+    admin_client.post(create_link, data)
+    assert not theme.css.exists()
+
+
+def test_css_link_is_created_with_correct_order(
+    theme, admin_client, create_link, css, data
+):
+    admin_client.post(create_link, data)
+    css_link = theme.css.get(name=data["name"])
+    assert css_link.order == 1
+
+
+def test_css_link_creation_queues_task_to_download_remote_css_size(
+    theme, admin_client, create_link, css, data, mock_task
+):
+    admin_client.post(create_link, data)
+    css_link = theme.css.last()
+    mock_task.assert_called_once_with(css_link.pk)
+
+
+def test_error_message_is_set_if_user_attempts_to_create_css_link_in_default_theme(
+    default_theme, admin_client
+):
+    create_link = reverse(
+        "misago:admin:appearance:themes:new-css-link", kwargs={"pk": default_theme.pk}
+    )
+    response = admin_client.get(create_link)
+    assert_has_error_message(response)
+
+
+def test_error_message_is_set_if_user_attempts_to_create_css_link_in_nonexisting_theme(
+    nonexisting_theme, admin_client
+):
+    create_link = reverse(
+        "misago:admin:appearance:themes:new-css-link",
+        kwargs={"pk": nonexisting_theme.pk},
+    )
+    response = admin_client.get(create_link)
+    assert_has_error_message(response)
+
+
+def test_css_link_creation_form_redirects_user_to_new_creation_form_after_creation(
+    theme, admin_client, create_link, data
+):
+    data["stay"] = "1"
+    response = admin_client.post(create_link, data)
+    assert response["location"] == reverse(
+        "misago:admin:appearance:themes:new-css-link", kwargs={"pk": theme.pk}
+    )
+
+
+def test_css_link_edition_form_is_displayed(admin_client, edit_link, css_link):
+    response = admin_client.get(edit_link)
+    assert response.status_code == 200
+    assert_contains(response, css_link.name)
+
+
+def test_css_link_name_can_be_changed(admin_client, edit_link, css_link, data):
+    data["name"] = "new link name"
+    admin_client.post(edit_link, data)
+
+    css_link.refresh_from_db()
+    assert css_link.name == data["name"]
+
+
+def test_css_link_url_can_be_changed(admin_client, edit_link, css_link, data):
+    data["url"] = "https://new.css-link.com/test.css"
+    admin_client.post(edit_link, data)
+
+    css_link.refresh_from_db()
+    assert css_link.url == data["url"]
+
+
+def test_changing_css_link_url_queues_task_to_download_remote_css_size(
+    admin_client, edit_link, css_link, data, mock_task
+):
+    data["url"] = "https://new.css-link.com/test.css"
+    admin_client.post(edit_link, data)
+    css_link.refresh_from_db()
+    mock_task.assert_called_once_with(css_link.pk)
+
+
+def test_not_changing_css_link_url_queues_task_to_download_remote_css_size(
+    admin_client, edit_link, css_link, data, mock_task
+):
+    admin_client.post(edit_link, data)
+    css_link.refresh_from_db()
+    mock_task.assert_not_called()
+
+
+def test_css_order_stays_the_same_after_edit(admin_client, edit_link, css_link, data):
+    original_order = css_link.order
+    data["name"] = "changed link"
+    admin_client.post(edit_link, data)
+
+    css_link.refresh_from_db()
+    assert css_link.order == original_order
+
+
+def test_error_message_is_set_if_user_attempts_to_edit_css_file_with_link_form(
+    theme, admin_client, css
+):
+    edit_link = reverse(
+        "misago:admin:appearance:themes:edit-css-link",
+        kwargs={"pk": theme.pk, "css_pk": css.pk},
+    )
+    response = admin_client.get(edit_link)
+    assert_has_error_message(response)

+ 5 - 0
misago/themes/admin/tests/test_deleting_assets.py

@@ -31,6 +31,11 @@ def test_theme_css_can_be_deleted(theme, delete_css, css):
     assert not theme.css.exists()
 
 
+def test_theme_css_link_can_be_deleted(theme, delete_css, css_link):
+    delete_css(theme, [css_link])
+    assert not theme.css.exists()
+
+
 def test_multiple_theme_css_can_be_deleted_at_single_time(
     theme, delete_css, css, css_link
 ):

+ 8 - 0
misago/themes/admin/validators.py

@@ -26,3 +26,11 @@ def validate_css_name(filename):
         raise ValidationError(
             _("Name has to contain at least one latin alphabet character or digit.")
         )
+
+
+def validate_css_name_is_available(instance, name):
+    queryset = instance.theme.css.filter(name=name)
+    if instance.pk:
+        queryset = queryset.exclude(pk=instance.pk)
+    if queryset.exists():
+        raise ValidationError(_("This name is already in use by other asset."))