+
{% include 'bookmarks/bookmark_list.html' %}
@@ -43,10 +39,16 @@
-
+
{% include 'bookmarks/tag_cloud.html' %}
+
+ {# Bookmark details #}
+
+ {% if details %}
+ {% include 'bookmarks/details/modal.html' %}
+ {% endif %}
+
{% endblock %}
diff --git a/bookmarks/templates/bookmarks/tag_modal.html b/bookmarks/templates/bookmarks/tag_modal.html
deleted file mode 100644
index 9549f1e..0000000
--- a/bookmarks/templates/bookmarks/tag_modal.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
- {% include 'bookmarks/tag_cloud.html' %}
-
-
-
-
diff --git a/bookmarks/templates/bookmarks/updates/bookmark_view_stream.html b/bookmarks/templates/bookmarks/updates/bookmark_view_stream.html
new file mode 100644
index 0000000..b009915
--- /dev/null
+++ b/bookmarks/templates/bookmarks/updates/bookmark_view_stream.html
@@ -0,0 +1,21 @@
+
+
+ {% include 'bookmarks/bookmark_list.html' %}
+
+
+
+
+
+ {% include 'bookmarks/tag_cloud.html' %}
+
+
+
+
+
+ {% if details %}
+ {% include 'bookmarks/details/modal.html' %}
+ {% endif %}
+
+
diff --git a/bookmarks/templates/bookmarks/updates/details-modal-frame.html b/bookmarks/templates/bookmarks/updates/details-modal-frame.html
new file mode 100644
index 0000000..80c51c4
--- /dev/null
+++ b/bookmarks/templates/bookmarks/updates/details-modal-frame.html
@@ -0,0 +1,10 @@
+
+{% include 'bookmarks/head.html' %}
+
+
+ {% if details %}
+ {% include 'bookmarks/details/modal.html' %}
+ {% endif %}
+
+
+
diff --git a/bookmarks/templates/bookmarks/user_select.html b/bookmarks/templates/bookmarks/user_select.html
index 1286702..2f16748 100644
--- a/bookmarks/templates/bookmarks/user_select.html
+++ b/bookmarks/templates/bookmarks/user_select.html
@@ -6,17 +6,10 @@
{% endfor %}
-
diff --git a/bookmarks/templates/settings/integrations.html b/bookmarks/templates/settings/integrations.html
index 0c925bf..66d9d6d 100644
--- a/bookmarks/templates/settings/integrations.html
+++ b/bookmarks/templates/settings/integrations.html
@@ -27,7 +27,7 @@
After saving the bookmark the linkding window closes and you are back on your website
Drag the following bookmarklet to your browser's toolbar:
-
📎 Add bookmark
@@ -43,7 +43,7 @@
Please treat this token as you would any other credential.
Any party with access to this token can access and manage all your bookmarks.
If you think that a token was compromised you can revoke (delete) it in the
admin panel.
+ target="_blank" href="{% url 'admin:authtoken_tokenproxy_changelist' %}">admin panel.
After deleting the token, a new one will be generated when you reload this settings page.
diff --git a/bookmarks/templatetags/bookmarks.py b/bookmarks/templatetags/bookmarks.py
index af7f929..d7706ce 100644
--- a/bookmarks/templatetags/bookmarks.py
+++ b/bookmarks/templatetags/bookmarks.py
@@ -6,8 +6,6 @@ from bookmarks.models import (
BookmarkForm,
BookmarkSearch,
BookmarkSearchForm,
- Tag,
- build_tag_string,
User,
)
@@ -34,9 +32,7 @@ def bookmark_form(
@register.inclusion_tag(
"bookmarks/search.html", name="bookmark_search", takes_context=True
)
-def bookmark_search(context, search: BookmarkSearch, tags: [Tag], mode: str = ""):
- tag_names = [tag.name for tag in tags]
- tags_string = build_tag_string(tag_names, " ")
+def bookmark_search(context, search: BookmarkSearch, mode: str = ""):
search_form = BookmarkSearchForm(search, editable_fields=["q"])
if mode == "shared":
@@ -50,7 +46,6 @@ def bookmark_search(context, search: BookmarkSearch, tags: [Tag], mode: str = ""
"search": search,
"search_form": search_form,
"preferences_form": preferences_form,
- "tags_string": tags_string,
"mode": mode,
}
diff --git a/bookmarks/templatetags/pagination.py b/bookmarks/templatetags/pagination.py
index eff5900..bb46e1e 100644
--- a/bookmarks/templatetags/pagination.py
+++ b/bookmarks/templatetags/pagination.py
@@ -2,6 +2,7 @@ from functools import reduce
from django import template
from django.core.paginator import Page
+from django.http import QueryDict
NUM_ADJACENT_PAGES = 2
@@ -12,11 +13,44 @@ register = template.Library()
"bookmarks/pagination.html", name="pagination", takes_context=True
)
def pagination(context, page: Page):
+ # remove page number and details from query parameters
+ query_params = context["request"].GET.copy()
+ query_params.pop("page", None)
+ query_params.pop("details", None)
+
+ prev_link = (
+ _generate_link(query_params, page.previous_page_number())
+ if page.has_previous()
+ else None
+ )
+ next_link = (
+ _generate_link(query_params, page.next_page_number())
+ if page.has_next()
+ else None
+ )
+
visible_page_numbers = get_visible_page_numbers(
page.number, page.paginator.num_pages
)
+ page_links = []
+ for page_number in visible_page_numbers:
+ if page_number == -1:
+ page_links.append(None)
+ else:
+ link = _generate_link(query_params, page_number)
+ page_links.append(
+ {
+ "active": page_number == page.number,
+ "number": page_number,
+ "link": link,
+ }
+ )
- return {"page": page, "visible_page_numbers": visible_page_numbers}
+ return {
+ "prev_link": prev_link,
+ "next_link": next_link,
+ "page_links": page_links,
+ }
def get_visible_page_numbers(current_page_number: int, num_pages: int) -> [int]:
@@ -56,3 +90,8 @@ def get_visible_page_numbers(current_page_number: int, num_pages: int) -> [int]:
return result
return reduce(append_page, visible_pages, [])
+
+
+def _generate_link(query_params: QueryDict, page_number: int) -> str:
+ query_params["page"] = page_number
+ return query_params.urlencode()
diff --git a/bookmarks/templatetags/shared.py b/bookmarks/templatetags/shared.py
index 6524c11..5da3c1c 100644
--- a/bookmarks/templatetags/shared.py
+++ b/bookmarks/templatetags/shared.py
@@ -28,12 +28,13 @@ def add_tag_to_query(context, tag_name: str):
params = context.request.GET.copy()
# Append to or create query string
- if params.__contains__("q"):
- query_string = params.__getitem__("q") + " "
- else:
- query_string = ""
- query_string = query_string + "#" + tag_name
- params.__setitem__("q", query_string)
+ query_string = params.get("q", "")
+ query_string = (query_string + " #" + tag_name).strip()
+ params.setlist("q", [query_string])
+
+ # Remove details ID and page number
+ params.pop("details", None)
+ params.pop("page", None)
return params.urlencode()
@@ -62,6 +63,10 @@ def remove_tag_from_query(context, tag_name: str):
query_string = " ".join(query_parts)
params.__setitem__("q", query_string)
+ # Remove details ID and page number
+ params.pop("details", None)
+ params.pop("page", None)
+
return params.urlencode()
diff --git a/bookmarks/tests/helpers.py b/bookmarks/tests/helpers.py
index dd97f2d..4a5f016 100644
--- a/bookmarks/tests/helpers.py
+++ b/bookmarks/tests/helpers.py
@@ -2,6 +2,7 @@ import random
import logging
from datetime import datetime
from typing import List
+from unittest import TestCase
from bs4 import BeautifulSoup
from django.contrib.auth.models import User
@@ -220,6 +221,75 @@ class HtmlTestMixin:
return BeautifulSoup(html, features="html.parser")
+class BookmarkListTestMixin(TestCase, HtmlTestMixin):
+ def assertVisibleBookmarks(
+ self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
+ ):
+ soup = self.make_soup(response.content.decode())
+ bookmark_list = soup.select_one(
+ f'ul.bookmark-list[data-bookmarks-total="{len(bookmarks)}"]'
+ )
+ self.assertIsNotNone(bookmark_list)
+
+ bookmark_items = bookmark_list.select("li[ld-bookmark-item]")
+ self.assertEqual(len(bookmark_items), len(bookmarks))
+
+ for bookmark in bookmarks:
+ bookmark_item = bookmark_list.select_one(
+ f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
+ )
+ self.assertIsNotNone(bookmark_item)
+
+ def assertInvisibleBookmarks(
+ self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
+ ):
+ soup = self.make_soup(response.content.decode())
+
+ for bookmark in bookmarks:
+ bookmark_item = soup.select_one(
+ f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
+ )
+ self.assertIsNone(bookmark_item)
+
+
+class TagCloudTestMixin(TestCase, HtmlTestMixin):
+ def assertVisibleTags(self, response, tags: List[Tag]):
+ soup = self.make_soup(response.content.decode())
+ tag_cloud = soup.select_one("div.tag-cloud")
+ self.assertIsNotNone(tag_cloud)
+
+ tag_items = tag_cloud.select("a[data-is-tag-item]")
+ self.assertEqual(len(tag_items), len(tags))
+
+ tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
+
+ for tag in tags:
+ self.assertTrue(tag.name in tag_item_names)
+
+ def assertInvisibleTags(self, response, tags: List[Tag]):
+ soup = self.make_soup(response.content.decode())
+ tag_items = soup.select("a[data-is-tag-item]")
+
+ tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
+
+ for tag in tags:
+ self.assertFalse(tag.name in tag_item_names)
+
+ def assertSelectedTags(self, response, tags: List[Tag]):
+ soup = self.make_soup(response.content.decode())
+ selected_tags = soup.select_one("p.selected-tags")
+ self.assertIsNotNone(selected_tags)
+
+ tag_list = selected_tags.select("a")
+ self.assertEqual(len(tag_list), len(tags))
+
+ for tag in tags:
+ self.assertTrue(
+ tag.name in selected_tags.text,
+ msg=f"Selected tags do not contain: {tag.name}",
+ )
+
+
class LinkdingApiTestCase(APITestCase):
def get(self, url, expected_status_code=status.HTTP_200_OK):
response = self.client.get(url)
diff --git a/bookmarks/tests/test_bookmark_action_view.py b/bookmarks/tests/test_bookmark_action_view.py
index 658cb5a..998a825 100644
--- a/bookmarks/tests/test_bookmark_action_view.py
+++ b/bookmarks/tests/test_bookmark_action_view.py
@@ -1,13 +1,24 @@
+from unittest.mock import patch
+
from django.contrib.auth.models import User
+from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import model_to_dict
-from django.test import TestCase
+from django.http import HttpResponse
+from django.test import TestCase, override_settings
from django.urls import reverse
-from bookmarks.models import Bookmark
-from bookmarks.tests.helpers import BookmarkFactoryMixin
+from bookmarks.models import Bookmark, BookmarkAsset
+from bookmarks.services import tasks, bookmarks
+from bookmarks.tests.helpers import (
+ BookmarkFactoryMixin,
+ BookmarkListTestMixin,
+ TagCloudTestMixin,
+)
-class BookmarkActionViewTestCase(TestCase, BookmarkFactoryMixin):
+class BookmarkActionViewTestCase(
+ TestCase, BookmarkFactoryMixin, BookmarkListTestMixin, TagCloudTestMixin
+):
def setUp(self) -> None:
user = self.get_or_create_test_user()
@@ -156,6 +167,129 @@ class BookmarkActionViewTestCase(TestCase, BookmarkFactoryMixin):
self.assertEqual(response.status_code, 404)
self.assertTrue(bookmark.shared)
+ @override_settings(LD_ENABLE_SNAPSHOTS=True)
+ def test_create_html_snapshot(self):
+ bookmark = self.setup_bookmark()
+ with patch.object(tasks, "_create_html_snapshot_task"):
+ self.client.post(
+ reverse("bookmarks:index.action"),
+ {
+ "create_html_snapshot": [bookmark.id],
+ },
+ )
+ self.assertEqual(bookmark.bookmarkasset_set.count(), 1)
+ asset = bookmark.bookmarkasset_set.first()
+ self.assertEqual(asset.asset_type, BookmarkAsset.TYPE_SNAPSHOT)
+
+ @override_settings(LD_ENABLE_SNAPSHOTS=True)
+ def test_can_only_create_html_snapshot_for_own_bookmarks(self):
+ other_user = self.setup_user()
+ bookmark = self.setup_bookmark(user=other_user)
+ with patch.object(tasks, "_create_html_snapshot_task"):
+ response = self.client.post(
+ reverse("bookmarks:index.action"),
+ {
+ "create_html_snapshot": [bookmark.id],
+ },
+ )
+ self.assertEqual(response.status_code, 404)
+ self.assertEqual(bookmark.bookmarkasset_set.count(), 0)
+
+ def test_upload_asset(self):
+ bookmark = self.setup_bookmark()
+ file_content = b"file content"
+ upload_file = SimpleUploadedFile("test.txt", file_content)
+
+ with patch.object(bookmarks, "upload_asset") as mock_upload_asset:
+ response = self.client.post(
+ reverse("bookmarks:index.action"),
+ {"upload_asset": bookmark.id, "upload_asset_file": upload_file},
+ )
+ self.assertEqual(response.status_code, 302)
+
+ mock_upload_asset.assert_called_once()
+
+ args, _ = mock_upload_asset.call_args
+ self.assertEqual(args[0], bookmark)
+
+ upload_file = args[1]
+ self.assertEqual(upload_file.name, "test.txt")
+
+ def test_can_only_upload_asset_for_own_bookmarks(self):
+ other_user = self.setup_user()
+ bookmark = self.setup_bookmark(user=other_user)
+ file_content = b"file content"
+ upload_file = SimpleUploadedFile("test.txt", file_content)
+
+ with patch.object(bookmarks, "upload_asset") as mock_upload_asset:
+ response = self.client.post(
+ reverse("bookmarks:index.action"),
+ {"upload_asset": bookmark.id, "upload_asset_file": upload_file},
+ )
+ self.assertEqual(response.status_code, 404)
+
+ mock_upload_asset.assert_not_called()
+
+ def test_remove_asset(self):
+ bookmark = self.setup_bookmark()
+ asset = self.setup_asset(bookmark)
+
+ response = self.client.post(
+ reverse("bookmarks:index.action"), {"remove_asset": asset.id}
+ )
+ self.assertEqual(response.status_code, 302)
+ self.assertFalse(BookmarkAsset.objects.filter(id=asset.id).exists())
+
+ def test_can_only_remove_own_asset(self):
+ other_user = self.setup_user()
+ bookmark = self.setup_bookmark(user=other_user)
+ asset = self.setup_asset(bookmark)
+
+ response = self.client.post(
+ reverse("bookmarks:index.action"), {"remove_asset": asset.id}
+ )
+ self.assertEqual(response.status_code, 404)
+ self.assertTrue(BookmarkAsset.objects.filter(id=asset.id).exists())
+
+ def test_update_state(self):
+ bookmark = self.setup_bookmark()
+
+ response = self.client.post(
+ reverse("bookmarks:index.action"),
+ {
+ "update_state": bookmark.id,
+ "is_archived": "on",
+ "unread": "on",
+ "shared": "on",
+ },
+ )
+ self.assertEqual(response.status_code, 302)
+
+ bookmark.refresh_from_db()
+ self.assertTrue(bookmark.unread)
+ self.assertTrue(bookmark.is_archived)
+ self.assertTrue(bookmark.shared)
+
+ def test_can_only_update_own_bookmark_state(self):
+ other_user = self.setup_user()
+ bookmark = self.setup_bookmark(user=other_user)
+
+ response = self.client.post(
+ reverse("bookmarks:index.action"),
+ {
+ "update_state": bookmark.id,
+ "is_archived": "on",
+ "unread": "on",
+ "shared": "on",
+ },
+ )
+ self.assertEqual(response.status_code, 404)
+
+ bookmark.refresh_from_db()
+ self.assertFalse(bookmark.unread)
+ self.assertFalse(bookmark.is_archived)
+ self.assertFalse(bookmark.shared)
+
def test_bulk_archive(self):
bookmark1 = self.setup_bookmark()
bookmark2 = self.setup_bookmark()
@@ -791,58 +925,119 @@ class BookmarkActionViewTestCase(TestCase, BookmarkFactoryMixin):
self.assertBookmarksAreUnmodified([bookmark1, bookmark2, bookmark3])
- def test_should_redirect_to_return_url(self):
- bookmark1 = self.setup_bookmark()
- bookmark2 = self.setup_bookmark()
- bookmark3 = self.setup_bookmark()
+ def test_index_action_redirects_to_index_with_query_params(self):
+ url = reverse("bookmarks:index.action") + "?q=foo&page=2"
+ redirect_url = reverse("bookmarks:index") + "?q=foo&page=2"
+ response = self.client.post(url)
- url = (
- reverse("bookmarks:index.action")
- + "?return_url="
- + reverse("bookmarks:settings.index")
- )
- response = self.client.post(
- url,
- {
- "bulk_action": ["bulk_archive"],
- "bulk_execute": [""],
- "bookmark_id": [
- str(bookmark1.id),
- str(bookmark2.id),
- str(bookmark3.id),
- ],
- },
- )
+ self.assertRedirects(response, redirect_url)
- self.assertRedirects(response, reverse("bookmarks:settings.index"))
+ def test_archived_action_redirects_to_archived_with_query_params(self):
+ url = reverse("bookmarks:archived.action") + "?q=foo&page=2"
+ redirect_url = reverse("bookmarks:archived") + "?q=foo&page=2"
+ response = self.client.post(url)
- def test_should_not_redirect_to_external_url(self):
- bookmark1 = self.setup_bookmark()
- bookmark2 = self.setup_bookmark()
- bookmark3 = self.setup_bookmark()
+ self.assertRedirects(response, redirect_url)
- def post_with(return_url, follow=None):
- url = reverse("bookmarks:index.action") + f"?return_url={return_url}"
- return self.client.post(
- url,
- {
- "bulk_action": ["bulk_archive"],
- "bulk_execute": [""],
- "bookmark_id": [
- str(bookmark1.id),
- str(bookmark2.id),
- str(bookmark3.id),
- ],
- },
- follow=follow,
+ def test_shared_action_redirects_to_shared_with_query_params(self):
+ url = reverse("bookmarks:shared.action") + "?q=foo&page=2"
+ redirect_url = reverse("bookmarks:shared") + "?q=foo&page=2"
+ response = self.client.post(url)
+
+ self.assertRedirects(response, redirect_url)
+
+ def bookmark_update_fixture(self):
+ user = self.get_or_create_test_user()
+ profile = user.profile
+ profile.enable_sharing = True
+ profile.save()
+
+ return {
+ "active": self.setup_numbered_bookmarks(3),
+ "archived": self.setup_numbered_bookmarks(3, archived=True),
+ "shared": self.setup_numbered_bookmarks(3, shared=True),
+ }
+
+ def assertBookmarkUpdateResponse(self, response: HttpResponse):
+ self.assertEqual(response.status_code, 200)
+
+ html = response.content.decode("utf-8")
+ soup = self.make_soup(html)
+
+ # bookmark list update
+ self.assertIsNotNone(
+ soup.select_one(
+ "turbo-stream[action='update'][target='bookmark-list-container']"
)
+ )
- response = post_with("https://example.com")
- self.assertRedirects(response, reverse("bookmarks:index"))
- response = post_with("//example.com")
- self.assertRedirects(response, reverse("bookmarks:index"))
- response = post_with("://example.com")
- self.assertRedirects(response, reverse("bookmarks:index"))
+ # tag cloud update
+ self.assertIsNotNone(
+ soup.select_one(
+ "turbo-stream[action='update'][target='tag-cloud-container']"
+ )
+ )
- response = post_with("/foo//example.com", follow=True)
- self.assertEqual(response.status_code, 404)
+ # update event
+ self.assertInHTML(
+ """
+
+ """,
+ html,
+ )
+
+ def test_index_action_with_turbo_returns_bookmark_update(self):
+ fixture = self.bookmark_update_fixture()
+ response = self.client.post(
+ reverse("bookmarks:index.action"),
+ HTTP_ACCEPT="text/vnd.turbo-stream.html",
+ )
+
+ visible_tags = self.get_tags_from_bookmarks(
+ fixture["active"] + fixture["shared"]
+ )
+ invisible_tags = self.get_tags_from_bookmarks(fixture["archived"])
+
+ self.assertBookmarkUpdateResponse(response)
+ self.assertVisibleBookmarks(response, fixture["active"] + fixture["shared"])
+ self.assertInvisibleBookmarks(response, fixture["archived"])
+ self.assertVisibleTags(response, visible_tags)
+ self.assertInvisibleTags(response, invisible_tags)
+
+ def test_archived_action_with_turbo_returns_bookmark_update(self):
+ fixture = self.bookmark_update_fixture()
+ response = self.client.post(
+ reverse("bookmarks:archived.action"),
+ HTTP_ACCEPT="text/vnd.turbo-stream.html",
+ )
+
+ visible_tags = self.get_tags_from_bookmarks(fixture["archived"])
+ invisible_tags = self.get_tags_from_bookmarks(
+ fixture["active"] + fixture["shared"]
+ )
+
+ self.assertBookmarkUpdateResponse(response)
+ self.assertVisibleBookmarks(response, fixture["archived"])
+ self.assertInvisibleBookmarks(response, fixture["active"] + fixture["shared"])
+ self.assertVisibleTags(response, visible_tags)
+ self.assertInvisibleTags(response, invisible_tags)
+
+ def test_shared_action_with_turbo_returns_bookmark_update(self):
+ fixture = self.bookmark_update_fixture()
+ response = self.client.post(
+ reverse("bookmarks:shared.action"),
+ HTTP_ACCEPT="text/vnd.turbo-stream.html",
+ )
+
+ visible_tags = self.get_tags_from_bookmarks(fixture["shared"])
+ invisible_tags = self.get_tags_from_bookmarks(
+ fixture["active"] + fixture["archived"]
+ )
+
+ self.assertBookmarkUpdateResponse(response)
+ self.assertVisibleBookmarks(response, fixture["shared"])
+ self.assertInvisibleBookmarks(response, fixture["active"] + fixture["archived"])
+ self.assertVisibleTags(response, visible_tags)
+ self.assertInvisibleTags(response, invisible_tags)
diff --git a/bookmarks/tests/test_bookmark_archived_view.py b/bookmarks/tests/test_bookmark_archived_view.py
index 4b357b6..040a5f8 100644
--- a/bookmarks/tests/test_bookmark_archived_view.py
+++ b/bookmarks/tests/test_bookmark_archived_view.py
@@ -1,89 +1,26 @@
import urllib.parse
-from typing import List
from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
-from bookmarks.models import Bookmark, BookmarkSearch, Tag, UserProfile
+from bookmarks.models import BookmarkSearch, UserProfile
from bookmarks.tests.helpers import (
BookmarkFactoryMixin,
- HtmlTestMixin,
+ BookmarkListTestMixin,
+ TagCloudTestMixin,
collapse_whitespace,
)
-class BookmarkArchivedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
+class BookmarkArchivedViewTestCase(
+ TestCase, BookmarkFactoryMixin, BookmarkListTestMixin, TagCloudTestMixin
+):
def setUp(self) -> None:
user = self.get_or_create_test_user()
self.client.force_login(user)
- def assertVisibleBookmarks(
- self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
- ):
- soup = self.make_soup(response.content.decode())
- bookmark_list = soup.select_one(
- f'ul.bookmark-list[data-bookmarks-total="{len(bookmarks)}"]'
- )
- self.assertIsNotNone(bookmark_list)
-
- bookmark_items = bookmark_list.select("li[ld-bookmark-item]")
- self.assertEqual(len(bookmark_items), len(bookmarks))
-
- for bookmark in bookmarks:
- bookmark_item = bookmark_list.select_one(
- f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
- )
- self.assertIsNotNone(bookmark_item)
-
- def assertInvisibleBookmarks(
- self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
- ):
- soup = self.make_soup(response.content.decode())
-
- for bookmark in bookmarks:
- bookmark_item = soup.select_one(
- f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
- )
- self.assertIsNone(bookmark_item)
-
- def assertVisibleTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- tag_cloud = soup.select_one("div.tag-cloud")
- self.assertIsNotNone(tag_cloud)
-
- tag_items = tag_cloud.select("a[data-is-tag-item]")
- self.assertEqual(len(tag_items), len(tags))
-
- tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
-
- for tag in tags:
- self.assertTrue(tag.name in tag_item_names)
-
- def assertInvisibleTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- tag_items = soup.select("a[data-is-tag-item]")
-
- tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
-
- for tag in tags:
- self.assertFalse(tag.name in tag_item_names)
-
- def assertSelectedTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- selected_tags = soup.select_one("p.selected-tags")
- self.assertIsNotNone(selected_tags)
-
- tag_list = selected_tags.select("a")
- self.assertEqual(len(tag_list), len(tags))
-
- for tag in tags:
- self.assertTrue(
- tag.name in selected_tags.text,
- msg=f"Selected tags do not contain: {tag.name}",
- )
-
def assertEditLink(self, response, url):
html = response.content.decode()
self.assertInHTML(
@@ -307,24 +244,21 @@ class BookmarkArchivedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
base_url = reverse("bookmarks:archived")
# without params
- return_url = urllib.parse.quote_plus(base_url)
- url = f"{action_url}?return_url={return_url}"
+ url = f"{action_url}"
response = self.client.get(base_url)
self.assertBulkActionForm(response, url)
# with query
url_params = "?q=foo"
- return_url = urllib.parse.quote_plus(base_url + url_params)
- url = f"{action_url}?q=foo&return_url={return_url}"
+ url = f"{action_url}?q=foo"
response = self.client.get(base_url + url_params)
self.assertBulkActionForm(response, url)
# with query and sort
url_params = "?q=foo&sort=title_asc"
- return_url = urllib.parse.quote_plus(base_url + url_params)
- url = f"{action_url}?q=foo&sort=title_asc&return_url={return_url}"
+ url = f"{action_url}?q=foo&sort=title_asc"
response = self.client.get(base_url + url_params)
self.assertBulkActionForm(response, url)
@@ -527,7 +461,7 @@ class BookmarkArchivedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
self.assertEqual(
actions_form.attrs["action"],
- "/bookmarks/archived/action?q=%23foo&return_url=%2Fbookmarks%2Farchived%3Fq%3D%2523foo",
+ "/bookmarks/archived/action?q=%23foo",
)
def test_encode_search_params(self):
@@ -557,3 +491,15 @@ class BookmarkArchivedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
url = reverse("bookmarks:archived") + "?page=alert(%27xss%27)"
response = self.client.get(url)
self.assertNotContains(response, "alert('xss')")
+
+ def test_turbo_frame_details_modal_renders_details_modal_update(self):
+ bookmark = self.setup_bookmark()
+ url = reverse("bookmarks:archived") + f"?bookmark_id={bookmark.id}"
+ response = self.client.get(url, headers={"Turbo-Frame": "details-modal"})
+
+ self.assertEqual(200, response.status_code)
+
+ soup = self.make_soup(response.content.decode())
+ self.assertIsNotNone(soup.select_one("turbo-frame#details-modal"))
+ self.assertIsNone(soup.select_one("#bookmark-list-container"))
+ self.assertIsNone(soup.select_one("#tag-cloud-container"))
diff --git a/bookmarks/tests/test_bookmark_details_modal.py b/bookmarks/tests/test_bookmark_details_modal.py
index a2ef4ad..291d149 100644
--- a/bookmarks/tests/test_bookmark_details_modal.py
+++ b/bookmarks/tests/test_bookmark_details_modal.py
@@ -1,14 +1,11 @@
import datetime
import re
-from unittest.mock import patch
-from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils import formats, timezone
from bookmarks.models import BookmarkAsset, UserProfile
-from bookmarks.services import bookmarks, tasks
from bookmarks.tests.helpers import BookmarkFactoryMixin, HtmlTestMixin
@@ -17,23 +14,23 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
user = self.get_or_create_test_user()
self.client.force_login(user)
- def get_view_name(self):
- return "bookmarks:details_modal"
-
- def get_base_url(self, bookmark):
- return reverse(self.get_view_name(), args=[bookmark.id])
-
def get_details_form(self, soup, bookmark):
- expected_url = reverse("bookmarks:details", args=[bookmark.id])
- return soup.find("form", {"action": expected_url})
+ form_url = reverse("bookmarks:index.action") + f"?details={bookmark.id}"
+ return soup.find("form", {"action": form_url, "enctype": "multipart/form-data"})
- def get_details(self, bookmark, return_url=""):
- url = self.get_base_url(bookmark)
- if return_url:
- url += f"?return_url={return_url}"
+ def get_index_details_modal(self, bookmark):
+ url = reverse("bookmarks:index") + f"?details={bookmark.id}"
response = self.client.get(url)
soup = self.make_soup(response.content)
- return soup
+ modal = soup.find("turbo-frame", {"id": "details-modal"})
+ return modal
+
+ def get_shared_details_modal(self, bookmark):
+ url = reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ response = self.client.get(url)
+ soup = self.make_soup(response.content)
+ modal = soup.find("turbo-frame", {"id": "details-modal"})
+ return modal
def find_section(self, soup, section_name):
dt = soup.find("dt", string=section_name)
@@ -54,35 +51,68 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def find_asset(self, soup, asset):
return soup.find("div", {"data-asset-id": asset.id})
- def details_route_access_test(self, view_name: str, shareable: bool):
+ def details_route_access_test(self):
# own bookmark
bookmark = self.setup_bookmark()
-
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
+ response = self.client.get(
+ reverse("bookmarks:index") + f"?details={bookmark.id}"
+ )
self.assertEqual(response.status_code, 200)
# other user's bookmark
other_user = self.setup_user()
bookmark = self.setup_bookmark(user=other_user)
-
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
+ response = self.client.get(
+ reverse("bookmarks:index") + f"?details={bookmark.id}"
+ )
self.assertEqual(response.status_code, 404)
- # non-existent bookmark
- response = self.client.get(reverse(view_name, args=[9999]))
- self.assertEqual(response.status_code, 404)
+ # non-existent bookmark - just returns without modal in response
+ response = self.client.get(reverse("bookmarks:index") + "?details=9999")
+ self.assertEqual(response.status_code, 200)
# guest user
self.client.logout()
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
- self.assertEqual(response.status_code, 404 if shareable else 302)
+ response = self.client.get(
+ reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 404)
- def details_route_sharing_access_test(self, view_name: str, shareable: bool):
+ def test_access(self):
+ # own bookmark
+ bookmark = self.setup_bookmark()
+ response = self.client.get(
+ reverse("bookmarks:index") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 200)
+
+ # other user's bookmark
+ other_user = self.setup_user()
+ bookmark = self.setup_bookmark(user=other_user)
+ response = self.client.get(
+ reverse("bookmarks:index") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 404)
+
+ # non-existent bookmark - just returns without modal in response
+ response = self.client.get(reverse("bookmarks:index") + "?details=9999")
+ self.assertEqual(response.status_code, 200)
+
+ # guest user
+ self.client.logout()
+ response = self.client.get(
+ reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 404)
+
+ def test_access_with_sharing(self):
# shared bookmark, sharing disabled
other_user = self.setup_user()
bookmark = self.setup_bookmark(shared=True, user=other_user)
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
+ response = self.client.get(
+ reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ )
self.assertEqual(response.status_code, 404)
# shared bookmark, sharing enabled
@@ -90,37 +120,31 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.enable_sharing = True
profile.save()
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
- self.assertEqual(response.status_code, 200 if shareable else 404)
+ response = self.client.get(
+ reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 200)
# shared bookmark, guest user, no public sharing
self.client.logout()
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
- self.assertEqual(response.status_code, 404 if shareable else 302)
+ response = self.client.get(
+ reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 404)
# shared bookmark, guest user, public sharing
profile.enable_public_sharing = True
profile.save()
- response = self.client.get(reverse(view_name, args=[bookmark.id]))
- self.assertEqual(response.status_code, 200 if shareable else 302)
-
- def test_access(self):
- self.details_route_access_test(self.get_view_name(), True)
-
- def test_access_with_sharing(self):
- self.details_route_sharing_access_test(self.get_view_name(), True)
-
- def test_assets_access(self):
- self.details_route_access_test("bookmarks:details_assets", True)
-
- def test_assets_access_with_sharing(self):
- self.details_route_sharing_access_test("bookmarks:details_assets", True)
+ response = self.client.get(
+ reverse("bookmarks:shared") + f"?details={bookmark.id}"
+ )
+ self.assertEqual(response.status_code, 200)
def test_displays_title(self):
# with title
bookmark = self.setup_bookmark(title="Test title")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
title = soup.find("h2")
self.assertIsNotNone(title)
@@ -128,7 +152,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# with website title
bookmark = self.setup_bookmark(title="", website_title="Website title")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
title = soup.find("h2")
self.assertIsNotNone(title)
@@ -136,7 +160,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# with URL only
bookmark = self.setup_bookmark(title="", website_title="")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
title = soup.find("h2")
self.assertIsNotNone(title)
@@ -145,7 +169,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_website_link(self):
# basics
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.url)
self.assertIsNotNone(link)
self.assertEqual(link["href"], bookmark.url)
@@ -153,7 +177,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# favicons disabled
bookmark = self.setup_bookmark(favicon_file="example.png")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.url)
image = link.select_one("img")
self.assertIsNone(image)
@@ -164,14 +188,14 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.save()
bookmark = self.setup_bookmark(favicon_file="")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.url)
image = link.select_one("img")
self.assertIsNone(image)
# favicons enabled, favicon present
bookmark = self.setup_bookmark(favicon_file="example.png")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.url)
image = link.select_one("img")
self.assertIsNotNone(image)
@@ -180,7 +204,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_reader_mode_link(self):
# no latest snapshot
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
self.assertEqual(self.count_weblinks(soup), 2)
# snapshot is not complete
@@ -194,7 +218,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
asset_type=BookmarkAsset.TYPE_SNAPSHOT,
status=BookmarkAsset.STATUS_FAILURE,
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
self.assertEqual(self.count_weblinks(soup), 2)
# not a snapshot
@@ -203,7 +227,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
asset_type="upload",
status=BookmarkAsset.STATUS_COMPLETE,
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
self.assertEqual(self.count_weblinks(soup), 2)
# snapshot is complete
@@ -212,7 +236,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
asset_type=BookmarkAsset.TYPE_SNAPSHOT,
status=BookmarkAsset.STATUS_COMPLETE,
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
self.assertEqual(self.count_weblinks(soup), 3)
reader_mode_url = reverse("bookmarks:assets.read", args=[asset.id])
@@ -221,7 +245,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_internet_archive_link_with_snapshot_url(self):
bookmark = self.setup_bookmark(web_archive_snapshot_url="https://example.com/")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.web_archive_snapshot_url)
self.assertIsNotNone(link)
self.assertEqual(link["href"], bookmark.web_archive_snapshot_url)
@@ -231,7 +255,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
bookmark = self.setup_bookmark(
web_archive_snapshot_url="https://example.com/", favicon_file="example.png"
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.web_archive_snapshot_url)
image = link.select_one("svg")
self.assertIsNone(image)
@@ -244,7 +268,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
bookmark = self.setup_bookmark(
web_archive_snapshot_url="https://example.com/", favicon_file=""
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.web_archive_snapshot_url)
image = link.select_one("svg")
self.assertIsNone(image)
@@ -253,7 +277,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
bookmark = self.setup_bookmark(
web_archive_snapshot_url="https://example.com/", favicon_file="example.png"
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, bookmark.web_archive_snapshot_url)
image = link.select_one("svg")
self.assertIsNotNone(image)
@@ -267,7 +291,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
"https://web.archive.org/web/20230811214511/https://example.com/"
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
link = self.find_weblink(soup, fallback_web_archive_url)
self.assertIsNotNone(link)
self.assertEqual(link["href"], fallback_web_archive_url)
@@ -281,7 +305,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.bookmark_link_target = UserProfile.BOOKMARK_LINK_TARGET_BLANK
profile.save()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
website_link = self.find_weblink(soup, bookmark.url)
self.assertIsNotNone(website_link)
@@ -297,7 +321,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.bookmark_link_target = UserProfile.BOOKMARK_LINK_TARGET_SELF
profile.save()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
website_link = self.find_weblink(soup, bookmark.url)
self.assertIsNotNone(website_link)
@@ -312,13 +336,13 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_preview_image(self):
# without image
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
image = soup.select_one("div.preview-image img")
self.assertIsNone(image)
# with image
bookmark = self.setup_bookmark(preview_image_file="example.png")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
image = soup.select_one("div.preview-image img")
self.assertIsNone(image)
@@ -328,13 +352,13 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.save()
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
image = soup.select_one("div.preview-image img")
self.assertIsNone(image)
# preview images enabled, image present
bookmark = self.setup_bookmark(preview_image_file="example.png")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
image = soup.select_one("div.preview-image img")
self.assertIsNotNone(image)
self.assertEqual(image["src"], "/static/example.png")
@@ -342,18 +366,15 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_status(self):
# renders form
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
form = self.get_details_form(soup, bookmark)
self.assertIsNotNone(form)
- self.assertEqual(
- form["action"], reverse("bookmarks:details", args=[bookmark.id])
- )
self.assertEqual(form["method"], "post")
# sharing disabled
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Status")
archived = section.find("input", {"type": "checkbox", "name": "is_archived"})
@@ -369,7 +390,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.save()
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Status")
archived = section.find("input", {"type": "checkbox", "name": "is_archived"})
@@ -381,7 +402,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# unchecked
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Status")
archived = section.find("input", {"type": "checkbox", "name": "is_archived"})
@@ -393,7 +414,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# checked
bookmark = self.setup_bookmark(is_archived=True, unread=True, shared=True)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Status")
archived = section.find("input", {"type": "checkbox", "name": "is_archived"})
@@ -406,106 +427,29 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_status_visibility(self):
# own bookmark
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.find_section(soup, "Status")
self.assertIsNotNone(section)
# other user's bookmark
other_user = self.setup_user(enable_sharing=True)
bookmark = self.setup_bookmark(user=other_user, shared=True)
- soup = self.get_details(bookmark)
+ soup = self.get_shared_details_modal(bookmark)
section = self.find_section(soup, "Status")
self.assertIsNone(section)
# guest user
self.client.logout()
+ other_user.profile.enable_public_sharing = True
+ other_user.profile.save()
bookmark = self.setup_bookmark(user=other_user, shared=True)
- soup = self.get_details(bookmark)
+ soup = self.get_shared_details_modal(bookmark)
section = self.find_section(soup, "Status")
self.assertIsNone(section)
- def test_status_update(self):
- bookmark = self.setup_bookmark()
-
- # update status
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "on", "unread": "on", "shared": "on"},
- )
- self.assertEqual(response.status_code, 302)
-
- bookmark.refresh_from_db()
- self.assertTrue(bookmark.is_archived)
- self.assertTrue(bookmark.unread)
- self.assertTrue(bookmark.shared)
-
- # update individual status
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "", "unread": "on", "shared": ""},
- )
- self.assertEqual(response.status_code, 302)
-
- bookmark.refresh_from_db()
- self.assertFalse(bookmark.is_archived)
- self.assertTrue(bookmark.unread)
- self.assertFalse(bookmark.shared)
-
- def test_status_update_access(self):
- # no sharing
- other_user = self.setup_user()
- bookmark = self.setup_bookmark(user=other_user)
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "on", "unread": "on", "shared": "on"},
- )
- self.assertEqual(response.status_code, 404)
-
- # shared, sharing disabled
- bookmark = self.setup_bookmark(user=other_user, shared=True)
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "on", "unread": "on", "shared": "on"},
- )
- self.assertEqual(response.status_code, 404)
-
- # shared, sharing enabled
- bookmark = self.setup_bookmark(user=other_user, shared=True)
- profile = other_user.profile
- profile.enable_sharing = True
- profile.save()
-
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "on", "unread": "on", "shared": "on"},
- )
- self.assertEqual(response.status_code, 404)
-
- # shared, public sharing enabled
- bookmark = self.setup_bookmark(user=other_user, shared=True)
- profile = other_user.profile
- profile.enable_public_sharing = True
- profile.save()
-
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "on", "unread": "on", "shared": "on"},
- )
- self.assertEqual(response.status_code, 404)
-
- # guest user
- self.client.logout()
- bookmark = self.setup_bookmark(user=other_user, shared=True)
-
- response = self.client.post(
- self.get_base_url(bookmark),
- {"is_archived": "on", "unread": "on", "shared": "on"},
- )
- self.assertEqual(response.status_code, 404)
-
def test_date_added(self):
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Date added")
expected_date = formats.date_format(bookmark.date_added, "DATETIME_FORMAT")
@@ -515,7 +459,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_tags(self):
# without tags
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.find_section(soup, "Tags")
self.assertIsNone(section)
@@ -523,7 +467,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# with tags
bookmark = self.setup_bookmark(tags=[self.setup_tag(), self.setup_tag()])
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Tags")
for tag in bookmark.tags.all():
@@ -535,14 +479,14 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_description(self):
# without description
bookmark = self.setup_bookmark(description="", website_description="")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.find_section(soup, "Description")
self.assertIsNone(section)
# with description
bookmark = self.setup_bookmark(description="Test description")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Description")
self.assertEqual(section.text.strip(), bookmark.description)
@@ -551,7 +495,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
bookmark = self.setup_bookmark(
description="", website_description="Website description"
)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Description")
self.assertEqual(section.text.strip(), bookmark.website_description)
@@ -559,14 +503,14 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_notes(self):
# without notes
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.find_section(soup, "Notes")
self.assertIsNone(section)
# with notes
bookmark = self.setup_bookmark(notes="Test notes")
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Notes")
self.assertEqual(section.decode_contents(), "
Test notes
")
@@ -575,52 +519,42 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
bookmark = self.setup_bookmark()
# with default return URL
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
edit_link = soup.find("a", string="Edit")
self.assertIsNotNone(edit_link)
- details_url = reverse("bookmarks:details", args=[bookmark.id])
- expected_url = (
- reverse("bookmarks:edit", args=[bookmark.id]) + "?return_url=" + details_url
- )
- self.assertEqual(edit_link["href"], expected_url)
-
- # with custom return URL
- soup = self.get_details(bookmark, return_url="/custom")
- edit_link = soup.find("a", string="Edit")
- self.assertIsNotNone(edit_link)
- expected_url = (
- reverse("bookmarks:edit", args=[bookmark.id]) + "?return_url=/custom"
- )
- self.assertEqual(edit_link["href"], expected_url)
+ details_url = reverse("bookmarks:index") + f"?details={bookmark.id}"
+ expected_url = "/bookmarks/1/edit?return_url=/bookmarks%3Fdetails%3D1"
+ self.assertEqual(expected_url, edit_link["href"])
def test_delete_button(self):
bookmark = self.setup_bookmark()
- # basics
- soup = self.get_details(bookmark)
- delete_button = soup.find("button", {"type": "submit", "name": "remove"})
+ modal = self.get_index_details_modal(bookmark)
+ delete_button = modal.find("button", {"type": "submit", "name": "remove"})
self.assertIsNotNone(delete_button)
- self.assertEqual(delete_button.text.strip(), "Delete...")
- self.assertEqual(delete_button["value"], str(bookmark.id))
+ self.assertEqual("Delete...", delete_button.text.strip())
+ self.assertEqual(str(bookmark.id), delete_button["value"])
form = delete_button.find_parent("form")
self.assertIsNotNone(form)
- expected_url = reverse("bookmarks:index.action") + f"?return_url=/bookmarks"
- self.assertEqual(form["action"], expected_url)
-
- # with custom return URL
- soup = self.get_details(bookmark, return_url="/custom")
- delete_button = soup.find("button", {"type": "submit", "name": "remove"})
- form = delete_button.find_parent("form")
- expected_url = reverse("bookmarks:index.action") + f"?return_url=/custom"
- self.assertEqual(form["action"], expected_url)
+ expected_url = reverse("bookmarks:index.action")
+ self.assertEqual(expected_url, form["action"])
def test_actions_visibility(self):
+ # own bookmark
+ bookmark = self.setup_bookmark()
+
+ soup = self.get_index_details_modal(bookmark)
+ edit_link = soup.find("a", string="Edit")
+ delete_button = soup.find("button", {"type": "submit", "name": "remove"})
+ self.assertIsNotNone(edit_link)
+ self.assertIsNotNone(delete_button)
+
# with sharing
other_user = self.setup_user(enable_sharing=True)
bookmark = self.setup_bookmark(user=other_user, shared=True)
- soup = self.get_details(bookmark)
+ soup = self.get_shared_details_modal(bookmark)
edit_link = soup.find("a", string="Edit")
delete_button = soup.find("button", {"type": "submit", "name": "remove"})
self.assertIsNone(edit_link)
@@ -632,7 +566,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
profile.save()
bookmark = self.setup_bookmark(user=other_user, shared=True)
- soup = self.get_details(bookmark)
+ soup = self.get_shared_details_modal(bookmark)
edit_link = soup.find("a", string="Edit")
delete_button = soup.find("button", {"type": "submit", "name": "remove"})
self.assertIsNone(edit_link)
@@ -642,7 +576,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
self.client.logout()
bookmark = self.setup_bookmark(user=other_user, shared=True)
- soup = self.get_details(bookmark)
+ soup = self.get_shared_details_modal(bookmark)
edit_link = soup.find("a", string="Edit")
delete_button = soup.find("button", {"type": "submit", "name": "remove"})
self.assertIsNone(edit_link)
@@ -651,7 +585,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_assets_visibility_no_snapshot_support(self):
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.find_section(soup, "Files")
self.assertIsNone(section)
@@ -659,7 +593,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def test_assets_visibility_with_snapshot_support(self):
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.find_section(soup, "Files")
self.assertIsNotNone(section)
@@ -668,7 +602,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# no assets
bookmark = self.setup_bookmark()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Files")
asset_list = section.find("div", {"class": "assets"})
self.assertIsNone(asset_list)
@@ -677,7 +611,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
bookmark = self.setup_bookmark()
self.setup_asset(bookmark)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Files")
asset_list = section.find("div", {"class": "assets"})
self.assertIsNotNone(asset_list)
@@ -691,7 +625,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
self.setup_asset(bookmark),
]
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
section = self.get_section(soup, "Files")
asset_list = section.find("div", {"class": "assets"})
@@ -717,7 +651,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
asset.file = ""
asset.save()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
asset_item = self.find_asset(soup, asset)
view_url = reverse("bookmarks:assets.view", args=[asset.id])
view_link = asset_item.find("a", {"href": view_url})
@@ -729,7 +663,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
pending_asset = self.setup_asset(bookmark, status=BookmarkAsset.STATUS_PENDING)
failed_asset = self.setup_asset(bookmark, status=BookmarkAsset.STATUS_FAILURE)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
asset_item = self.find_asset(soup, pending_asset)
asset_text = asset_item.select_one(".asset-text span")
@@ -746,7 +680,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
asset2 = self.setup_asset(bookmark, file_size=54639)
asset3 = self.setup_asset(bookmark, file_size=11492020)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
asset_item = self.find_asset(soup, asset1)
asset_text = asset_item.select_one(".asset-text")
@@ -766,7 +700,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# with file
asset = self.setup_asset(bookmark)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
asset_item = self.find_asset(soup, asset)
view_link = asset_item.find("a", string="View")
@@ -779,7 +713,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# without file
asset.file = ""
asset.save()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
asset_item = self.find_asset(soup, asset)
view_link = asset_item.find("a", string="View")
@@ -793,7 +727,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
other_user = self.setup_user(enable_sharing=True, enable_public_sharing=True)
bookmark = self.setup_bookmark(shared=True, user=other_user)
asset = self.setup_asset(bookmark)
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
asset_item = self.find_asset(soup, asset)
view_link = asset_item.find("a", string="View")
@@ -805,7 +739,7 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
# shared bookmark, guest user
self.client.logout()
- soup = self.get_details(bookmark)
+ soup = self.get_shared_details_modal(bookmark)
asset_item = self.find_asset(soup, asset)
view_link = asset_item.find("a", string="View")
@@ -815,77 +749,13 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
self.assertIsNotNone(view_link)
self.assertIsNone(delete_button)
- def test_remove_asset(self):
- # remove asset
- bookmark = self.setup_bookmark()
- asset = self.setup_asset(bookmark)
-
- response = self.client.post(
- self.get_base_url(bookmark), {"remove_asset": asset.id}
- )
- self.assertEqual(response.status_code, 302)
- self.assertFalse(BookmarkAsset.objects.filter(id=asset.id).exists())
-
- # non-existent asset
- response = self.client.post(self.get_base_url(bookmark), {"remove_asset": 9999})
- self.assertEqual(response.status_code, 404)
-
- # post without asset ID does not remove
- asset = self.setup_asset(bookmark)
- response = self.client.post(self.get_base_url(bookmark))
- self.assertEqual(response.status_code, 302)
- self.assertTrue(BookmarkAsset.objects.filter(id=asset.id).exists())
-
- # guest user
- asset = self.setup_asset(bookmark)
- self.client.logout()
- response = self.client.post(
- self.get_base_url(bookmark), {"remove_asset": asset.id}
- )
- self.assertEqual(response.status_code, 404)
- self.assertTrue(BookmarkAsset.objects.filter(id=asset.id).exists())
-
- @override_settings(LD_ENABLE_SNAPSHOTS=True)
- def test_assets_refresh_when_having_pending_asset(self):
- bookmark = self.setup_bookmark()
- asset = self.setup_asset(bookmark, status=BookmarkAsset.STATUS_COMPLETE)
- fetch_url = reverse("bookmarks:details_assets", args=[bookmark.id])
-
- # no pending asset
- soup = self.get_details(bookmark)
- files_section = self.find_section(soup, "Files")
- assets_wrapper = files_section.find("div", {"ld-fetch": fetch_url})
- self.assertIsNone(assets_wrapper)
-
- # with pending asset
- asset.status = BookmarkAsset.STATUS_PENDING
- asset.save()
-
- soup = self.get_details(bookmark)
- files_section = self.find_section(soup, "Files")
- assets_wrapper = files_section.find("div", {"ld-fetch": fetch_url})
- self.assertIsNotNone(assets_wrapper)
-
- @override_settings(LD_ENABLE_SNAPSHOTS=True)
- def test_create_snapshot(self):
- with patch.object(
- tasks, "_create_html_snapshot_task"
- ) as mock_create_html_snapshot_task:
- bookmark = self.setup_bookmark()
- response = self.client.post(
- self.get_base_url(bookmark), {"create_snapshot": ""}
- )
- self.assertEqual(response.status_code, 302)
-
- self.assertEqual(bookmark.bookmarkasset_set.count(), 1)
-
@override_settings(LD_ENABLE_SNAPSHOTS=True)
def test_create_snapshot_is_disabled_when_having_pending_asset(self):
bookmark = self.setup_bookmark()
asset = self.setup_asset(bookmark, status=BookmarkAsset.STATUS_COMPLETE)
# no pending asset
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
files_section = self.find_section(soup, "Files")
create_button = files_section.find(
"button", string=re.compile("Create HTML snapshot")
@@ -896,40 +766,9 @@ class BookmarkDetailsModalTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
asset.status = BookmarkAsset.STATUS_PENDING
asset.save()
- soup = self.get_details(bookmark)
+ soup = self.get_index_details_modal(bookmark)
files_section = self.find_section(soup, "Files")
create_button = files_section.find(
"button", string=re.compile("Create HTML snapshot")
)
self.assertTrue(create_button.has_attr("disabled"))
-
- def test_upload_file(self):
- bookmark = self.setup_bookmark()
- file_content = b"file content"
- upload_file = SimpleUploadedFile("test.txt", file_content)
-
- with patch.object(bookmarks, "upload_asset") as mock_upload_asset:
- response = self.client.post(
- self.get_base_url(bookmark),
- {"upload_asset": "", "upload_asset_file": upload_file},
- )
- self.assertEqual(response.status_code, 302)
-
- mock_upload_asset.assert_called_once()
-
- args, kwargs = mock_upload_asset.call_args
- self.assertEqual(args[0], bookmark)
-
- upload_file = args[1]
- self.assertEqual(upload_file.name, "test.txt")
-
- def test_upload_file_without_file(self):
- bookmark = self.setup_bookmark()
-
- with patch.object(bookmarks, "upload_asset") as mock_upload_asset:
- response = self.client.post(
- self.get_base_url(bookmark),
- {"upload_asset": ""},
- )
- self.assertEqual(response.status_code, 400)
- mock_upload_asset.assert_not_called()
diff --git a/bookmarks/tests/test_bookmark_details_view.py b/bookmarks/tests/test_bookmark_details_view.py
deleted file mode 100644
index 36824de..0000000
--- a/bookmarks/tests/test_bookmark_details_view.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from bookmarks.tests.test_bookmark_details_modal import BookmarkDetailsModalTestCase
-
-
-class BookmarkDetailsViewTestCase(BookmarkDetailsModalTestCase):
- def get_view_name(self):
- return "bookmarks:details"
diff --git a/bookmarks/tests/test_bookmark_index_view.py b/bookmarks/tests/test_bookmark_index_view.py
index 608f281..8a0c680 100644
--- a/bookmarks/tests/test_bookmark_index_view.py
+++ b/bookmarks/tests/test_bookmark_index_view.py
@@ -1,85 +1,24 @@
import urllib.parse
-from typing import List
from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
-from bookmarks.models import Bookmark, BookmarkSearch, Tag, UserProfile
-from bookmarks.tests.helpers import BookmarkFactoryMixin, HtmlTestMixin
+from bookmarks.models import BookmarkSearch, UserProfile
+from bookmarks.tests.helpers import (
+ BookmarkFactoryMixin,
+ BookmarkListTestMixin,
+ TagCloudTestMixin,
+)
-class BookmarkIndexViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
-
+class BookmarkIndexViewTestCase(
+ TestCase, BookmarkFactoryMixin, BookmarkListTestMixin, TagCloudTestMixin
+):
def setUp(self) -> None:
user = self.get_or_create_test_user()
self.client.force_login(user)
- def assertVisibleBookmarks(
- self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
- ):
- soup = self.make_soup(response.content.decode())
- bookmark_list = soup.select_one(
- f'ul.bookmark-list[data-bookmarks-total="{len(bookmarks)}"]'
- )
- self.assertIsNotNone(bookmark_list)
-
- bookmark_items = bookmark_list.select("li[ld-bookmark-item]")
- self.assertEqual(len(bookmark_items), len(bookmarks))
-
- for bookmark in bookmarks:
- bookmark_item = bookmark_list.select_one(
- f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
- )
- self.assertIsNotNone(bookmark_item)
-
- def assertInvisibleBookmarks(
- self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
- ):
- soup = self.make_soup(response.content.decode())
-
- for bookmark in bookmarks:
- bookmark_item = soup.select_one(
- f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
- )
- self.assertIsNone(bookmark_item)
-
- def assertVisibleTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- tag_cloud = soup.select_one("div.tag-cloud")
- self.assertIsNotNone(tag_cloud)
-
- tag_items = tag_cloud.select("a[data-is-tag-item]")
- self.assertEqual(len(tag_items), len(tags))
-
- tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
-
- for tag in tags:
- self.assertTrue(tag.name in tag_item_names)
-
- def assertInvisibleTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- tag_items = soup.select("a[data-is-tag-item]")
-
- tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
-
- for tag in tags:
- self.assertFalse(tag.name in tag_item_names)
-
- def assertSelectedTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- selected_tags = soup.select_one("p.selected-tags")
- self.assertIsNotNone(selected_tags)
-
- tag_list = selected_tags.select("a")
- self.assertEqual(len(tag_list), len(tags))
-
- for tag in tags:
- self.assertTrue(
- tag.name in selected_tags.text,
- msg=f"Selected tags do not contain: {tag.name}",
- )
-
def assertEditLink(self, response, url):
html = response.content.decode()
self.assertInHTML(
@@ -285,24 +224,21 @@ class BookmarkIndexViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
base_url = reverse("bookmarks:index")
# without params
- return_url = urllib.parse.quote_plus(base_url)
- url = f"{action_url}?return_url={return_url}"
+ url = f"{action_url}"
response = self.client.get(base_url)
self.assertBulkActionForm(response, url)
# with query
url_params = "?q=foo"
- return_url = urllib.parse.quote_plus(base_url + url_params)
- url = f"{action_url}?q=foo&return_url={return_url}"
+ url = f"{action_url}?q=foo"
response = self.client.get(base_url + url_params)
self.assertBulkActionForm(response, url)
# with query and sort
url_params = "?q=foo&sort=title_asc"
- return_url = urllib.parse.quote_plus(base_url + url_params)
- url = f"{action_url}?q=foo&sort=title_asc&return_url={return_url}"
+ url = f"{action_url}?q=foo&sort=title_asc"
response = self.client.get(base_url + url_params)
self.assertBulkActionForm(response, url)
@@ -503,7 +439,7 @@ class BookmarkIndexViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
self.assertEqual(
actions_form.attrs["action"],
- "/bookmarks/action?q=%23foo&return_url=%2Fbookmarks%3Fq%3D%2523foo",
+ "/bookmarks/action?q=%23foo",
)
def test_encode_search_params(self):
@@ -533,3 +469,15 @@ class BookmarkIndexViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
url = reverse("bookmarks:index") + "?page=alert(%27xss%27)"
response = self.client.get(url)
self.assertNotContains(response, "alert('xss')")
+
+ def test_turbo_frame_details_modal_renders_details_modal_update(self):
+ bookmark = self.setup_bookmark()
+ url = reverse("bookmarks:index") + f"?bookmark_id={bookmark.id}"
+ response = self.client.get(url, headers={"Turbo-Frame": "details-modal"})
+
+ self.assertEqual(200, response.status_code)
+
+ soup = self.make_soup(response.content.decode())
+ self.assertIsNotNone(soup.select_one("turbo-frame#details-modal"))
+ self.assertIsNone(soup.select_one("#bookmark-list-container"))
+ self.assertIsNone(soup.select_one("#tag-cloud-container"))
diff --git a/bookmarks/tests/test_bookmark_search_tag.py b/bookmarks/tests/test_bookmark_search_tag.py
index d40bf47..e8c016f 100644
--- a/bookmarks/tests/test_bookmark_search_tag.py
+++ b/bookmarks/tests/test_bookmark_search_tag.py
@@ -1,16 +1,13 @@
from bs4 import BeautifulSoup
-from django.db.models import QuerySet
from django.template import Template, RequestContext
from django.test import TestCase, RequestFactory
-from bookmarks.models import BookmarkSearch, Tag
+from bookmarks.models import BookmarkSearch
from bookmarks.tests.helpers import BookmarkFactoryMixin, HtmlTestMixin
class BookmarkSearchTagTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
- def render_template(
- self, url: str, tags: QuerySet[Tag] = Tag.objects.all(), mode: str = ""
- ):
+ def render_template(self, url: str, mode: str = ""):
rf = RequestFactory()
request = rf.get(url)
request.user = self.get_or_create_test_user()
@@ -21,32 +18,31 @@ class BookmarkSearchTagTest(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
{
"request": request,
"search": search,
- "tags": tags,
"mode": mode,
},
)
template_to_render = Template(
- "{% load bookmarks %}" "{% bookmark_search search tags mode %}"
+ "{% load bookmarks %} {% bookmark_search search mode %}"
)
return template_to_render.render(context)
def assertHiddenInput(self, form: BeautifulSoup, name: str, value: str = None):
- input = form.select_one(f'input[name="{name}"][type="hidden"]')
- self.assertIsNotNone(input)
+ element = form.select_one(f'input[name="{name}"][type="hidden"]')
+ self.assertIsNotNone(element)
if value is not None:
- self.assertEqual(input["value"], value)
+ self.assertEqual(element["value"], value)
def assertNoHiddenInput(self, form: BeautifulSoup, name: str):
- input = form.select_one(f'input[name="{name}"][type="hidden"]')
- self.assertIsNone(input)
+ element = form.select_one(f'input[name="{name}"][type="hidden"]')
+ self.assertIsNone(element)
def assertSearchInput(self, form: BeautifulSoup, name: str, value: str = None):
- input = form.select_one(f'input[name="{name}"][type="search"]')
- self.assertIsNotNone(input)
+ element = form.select_one(f'input[name="{name}"][type="search"]')
+ self.assertIsNotNone(element)
if value is not None:
- self.assertEqual(input["value"], value)
+ self.assertEqual(element["value"], value)
def assertSelect(self, form: BeautifulSoup, name: str, value: str = None):
select = form.select_one(f'select[name="{name}"]')
diff --git a/bookmarks/tests/test_bookmark_shared_view.py b/bookmarks/tests/test_bookmark_shared_view.py
index a20fd2f..c54886b 100644
--- a/bookmarks/tests/test_bookmark_shared_view.py
+++ b/bookmarks/tests/test_bookmark_shared_view.py
@@ -6,11 +6,16 @@ from django.test import TestCase
from django.urls import reverse
from bookmarks.models import Bookmark, BookmarkSearch, Tag, UserProfile
-from bookmarks.tests.helpers import BookmarkFactoryMixin, HtmlTestMixin
+from bookmarks.tests.helpers import (
+ BookmarkFactoryMixin,
+ BookmarkListTestMixin,
+ TagCloudTestMixin,
+)
-class BookmarkSharedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
-
+class BookmarkSharedViewTestCase(
+ TestCase, BookmarkFactoryMixin, BookmarkListTestMixin, TagCloudTestMixin
+):
def authenticate(self) -> None:
user = self.get_or_create_test_user()
self.client.force_login(user)
@@ -24,57 +29,6 @@ class BookmarkSharedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
count=count,
)
- def assertVisibleBookmarks(
- self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
- ):
- soup = self.make_soup(response.content.decode())
- bookmark_list = soup.select_one(
- f'ul.bookmark-list[data-bookmarks-total="{len(bookmarks)}"]'
- )
- self.assertIsNotNone(bookmark_list)
-
- bookmark_items = bookmark_list.select("li[ld-bookmark-item]")
- self.assertEqual(len(bookmark_items), len(bookmarks))
-
- for bookmark in bookmarks:
- bookmark_item = bookmark_list.select_one(
- f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
- )
- self.assertIsNotNone(bookmark_item)
-
- def assertInvisibleBookmarks(
- self, response, bookmarks: List[Bookmark], link_target: str = "_blank"
- ):
- soup = self.make_soup(response.content.decode())
-
- for bookmark in bookmarks:
- bookmark_item = soup.select_one(
- f'li[ld-bookmark-item] a[href="{bookmark.url}"][target="{link_target}"]'
- )
- self.assertIsNone(bookmark_item)
-
- def assertVisibleTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- tag_cloud = soup.select_one("div.tag-cloud")
- self.assertIsNotNone(tag_cloud)
-
- tag_items = tag_cloud.select("a[data-is-tag-item]")
- self.assertEqual(len(tag_items), len(tags))
-
- tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
-
- for tag in tags:
- self.assertTrue(tag.name in tag_item_names)
-
- def assertInvisibleTags(self, response, tags: List[Tag]):
- soup = self.make_soup(response.content.decode())
- tag_items = soup.select("a[data-is-tag-item]")
-
- tag_item_names = [tag_item.text.strip() for tag_item in tag_items]
-
- for tag in tags:
- self.assertFalse(tag.name in tag_item_names)
-
def assertVisibleUserOptions(self, response, users: List[User]):
html = response.content.decode()
@@ -84,7 +38,7 @@ class BookmarkSharedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
f'
'
)
user_select_html = f"""
-