- {% if has_selected_tags %}
+ {% if tag_cloud.has_selected_tags %}
- {% for group in groups %}
+ {% for group in tag_cloud.groups %}
{% for tag in group.tags %}
{# Highlight first char of first tag in group #}
diff --git a/bookmarks/templatetags/bookmarks.py b/bookmarks/templatetags/bookmarks.py
index 793c520..fd4754e 100644
--- a/bookmarks/templatetags/bookmarks.py
+++ b/bookmarks/templatetags/bookmarks.py
@@ -1,10 +1,8 @@
-from typing import List, Set
+from typing import List
from django import template
-from django.core.paginator import Page
from bookmarks.models import BookmarkForm, BookmarkFilters, Tag, build_tag_string, User
-from bookmarks.utils import unique
register = template.Library()
@@ -20,60 +18,6 @@ def bookmark_form(context, form: BookmarkForm, cancel_url: str, bookmark_id: int
}
-class TagGroup:
- def __init__(self, char):
- self.tags = []
- self.char = char
-
-
-def create_tag_groups(tags: Set[Tag]):
- # Ensure groups, as well as tags within groups, are ordered alphabetically
- sorted_tags = sorted(tags, key=lambda x: str.lower(x.name))
- group = None
- groups = []
-
- # Group tags that start with a different character than the previous one
- for tag in sorted_tags:
- tag_char = tag.name[0].lower()
-
- if not group or group.char != tag_char:
- group = TagGroup(tag_char)
- groups.append(group)
-
- group.tags.append(tag)
-
- return groups
-
-
-@register.inclusion_tag('bookmarks/tag_cloud.html', name='tag_cloud', takes_context=True)
-def tag_cloud(context, tags: List[Tag], selected_tags: List[Tag]):
- # Only display each tag name once, ignoring casing
- # This covers cases where the tag cloud contains shared tags with duplicate names
- # Also means that the cloud can not make assumptions that it will necessarily contain
- # all tags of the current user
- unique_tags = unique(tags, key=lambda x: str.lower(x.name))
- unique_selected_tags = unique(selected_tags, key=lambda x: str.lower(x.name))
-
- has_selected_tags = len(unique_selected_tags) > 0
- unselected_tags = set(unique_tags).symmetric_difference(unique_selected_tags)
- groups = create_tag_groups(unselected_tags)
- return {
- 'groups': groups,
- 'selected_tags': unique_selected_tags,
- 'has_selected_tags': has_selected_tags,
- }
-
-
-@register.inclusion_tag('bookmarks/bookmark_list.html', name='bookmark_list', takes_context=True)
-def bookmark_list(context, bookmarks: Page, return_url: str, link_target: str = '_blank'):
- return {
- 'request': context['request'],
- 'bookmarks': bookmarks,
- 'return_url': return_url,
- 'link_target': link_target,
- }
-
-
@register.inclusion_tag('bookmarks/search.html', name='bookmark_search', takes_context=True)
def bookmark_search(context, filters: BookmarkFilters, tags: [Tag], mode: str = ''):
tag_names = [tag.name for tag in tags]
diff --git a/bookmarks/tests/helpers.py b/bookmarks/tests/helpers.py
index e72f2fd..eb8a6b6 100644
--- a/bookmarks/tests/helpers.py
+++ b/bookmarks/tests/helpers.py
@@ -71,6 +71,44 @@ class BookmarkFactoryMixin:
bookmark.save()
return bookmark
+ def setup_numbered_bookmarks(self,
+ count: int,
+ prefix: str = '',
+ suffix: str = '',
+ tag_prefix: str = '',
+ archived: bool = False,
+ shared: bool = False,
+ with_tags: bool = False,
+ user: User = None):
+ user = user or self.get_or_create_test_user()
+
+ if not prefix:
+ if archived:
+ prefix = 'Archived Bookmark'
+ elif shared:
+ prefix = 'Shared Bookmark'
+ else:
+ prefix = 'Bookmark'
+
+ if not tag_prefix:
+ if archived:
+ tag_prefix = 'Archived Tag'
+ elif shared:
+ tag_prefix = 'Shared Tag'
+ else:
+ tag_prefix = 'Tag'
+
+ for i in range(1, count + 1):
+ title = f'{prefix} {i}{suffix}'
+ tags = []
+ if with_tags:
+ tag_name = f'{tag_prefix} {i}{suffix}'
+ tags = [self.setup_tag(name=tag_name)]
+ self.setup_bookmark(title=title, is_archived=archived, shared=shared, tags=tags, user=user)
+
+ def get_numbered_bookmark(self, title: str):
+ return Bookmark.objects.get(title=title)
+
def setup_tag(self, user: User = None, name: str = ''):
if user is None:
user = self.get_or_create_test_user()
diff --git a/bookmarks/tests/test_bookmark_archived_view.py b/bookmarks/tests/test_bookmark_archived_view.py
index 98a2fef..b293c2d 100644
--- a/bookmarks/tests/test_bookmark_archived_view.py
+++ b/bookmarks/tests/test_bookmark_archived_view.py
@@ -16,7 +16,7 @@ class BookmarkArchivedViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin
def assertVisibleBookmarks(self, response, bookmarks: List[Bookmark], link_target: str = '_blank'):
html = response.content.decode()
- self.assertContains(response, 'data-is-bookmark-item', count=len(bookmarks))
+ self.assertContains(response, '
', count=len(bookmarks))
for bookmark in bookmarks:
self.assertInHTML(
diff --git a/bookmarks/tests/test_bookmark_archived_view_performance.py b/bookmarks/tests/test_bookmark_archived_view_performance.py
index 72159ac..3336e9f 100644
--- a/bookmarks/tests/test_bookmark_archived_view_performance.py
+++ b/bookmarks/tests/test_bookmark_archived_view_performance.py
@@ -27,7 +27,7 @@ class BookmarkArchivedViewPerformanceTestCase(TransactionTestCase, BookmarkFacto
context = CaptureQueriesContext(self.get_connection())
with context:
response = self.client.get(reverse('bookmarks:archived'))
- self.assertContains(response, 'data-is-bookmark-item', num_initial_bookmarks)
+ self.assertContains(response, ' ', num_initial_bookmarks)
number_of_queries = context.final_queries
@@ -39,4 +39,4 @@ class BookmarkArchivedViewPerformanceTestCase(TransactionTestCase, BookmarkFacto
# assert num queries doesn't increase
with self.assertNumQueries(number_of_queries):
response = self.client.get(reverse('bookmarks:archived'))
- self.assertContains(response, 'data-is-bookmark-item', num_initial_bookmarks + num_additional_bookmarks)
+ self.assertContains(response, ' ', num_initial_bookmarks + num_additional_bookmarks)
diff --git a/bookmarks/tests/test_bookmark_edit_view.py b/bookmarks/tests/test_bookmark_edit_view.py
index 93b4c20..99b86e7 100644
--- a/bookmarks/tests/test_bookmark_edit_view.py
+++ b/bookmarks/tests/test_bookmark_edit_view.py
@@ -89,7 +89,7 @@ class BookmarkEditViewTestCase(TestCase, BookmarkFactoryMixin):
tag_string = build_tag_string(bookmark.tag_names, ' ')
self.assertInHTML(f'''
-
''', html)
diff --git a/bookmarks/tests/test_bookmark_index_view.py b/bookmarks/tests/test_bookmark_index_view.py
index b96970a..c92c5a1 100644
--- a/bookmarks/tests/test_bookmark_index_view.py
+++ b/bookmarks/tests/test_bookmark_index_view.py
@@ -17,7 +17,7 @@ class BookmarkIndexViewTestCase(TestCase, BookmarkFactoryMixin, HtmlTestMixin):
def assertVisibleBookmarks(self, response, bookmarks: List[Bookmark], link_target: str = '_blank'):
html = response.content.decode()
- self.assertContains(response, 'data-is-bookmark-item', count=len(bookmarks))
+ self.assertContains(response, ' ', count=len(bookmarks))
for bookmark in bookmarks:
self.assertInHTML(
diff --git a/bookmarks/tests/test_bookmark_index_view_performance.py b/bookmarks/tests/test_bookmark_index_view_performance.py
index f13f532..122b00f 100644
--- a/bookmarks/tests/test_bookmark_index_view_performance.py
+++ b/bookmarks/tests/test_bookmark_index_view_performance.py
@@ -27,7 +27,7 @@ class BookmarkIndexViewPerformanceTestCase(TransactionTestCase, BookmarkFactoryM
context = CaptureQueriesContext(self.get_connection())
with context:
response = self.client.get(reverse('bookmarks:index'))
- self.assertContains(response, 'data-is-bookmark-item', num_initial_bookmarks)
+ self.assertContains(response, ' ', num_initial_bookmarks)
number_of_queries = context.final_queries
@@ -39,4 +39,4 @@ class BookmarkIndexViewPerformanceTestCase(TransactionTestCase, BookmarkFactoryM
# assert num queries doesn't increase
with self.assertNumQueries(number_of_queries):
response = self.client.get(reverse('bookmarks:index'))
- self.assertContains(response, 'data-is-bookmark-item', num_initial_bookmarks + num_additional_bookmarks)
+ self.assertContains(response, ' ', num_initial_bookmarks + num_additional_bookmarks)
diff --git a/bookmarks/tests/test_bookmark_shared_view.py b/bookmarks/tests/test_bookmark_shared_view.py
index b94156a..eb393e5 100644
--- a/bookmarks/tests/test_bookmark_shared_view.py
+++ b/bookmarks/tests/test_bookmark_shared_view.py
@@ -22,7 +22,7 @@ class BookmarkSharedViewTestCase(TestCase, BookmarkFactoryMixin):
def assertVisibleBookmarks(self, response, bookmarks: List[Bookmark], link_target: str = '_blank'):
html = response.content.decode()
- self.assertContains(response, 'data-is-bookmark-item', count=len(bookmarks))
+ self.assertContains(response, ' ', count=len(bookmarks))
for bookmark in bookmarks:
self.assertBookmarkCount(html, bookmark, 1, link_target)
diff --git a/bookmarks/tests/test_bookmark_shared_view_performance.py b/bookmarks/tests/test_bookmark_shared_view_performance.py
index f9aaceb..588442c 100644
--- a/bookmarks/tests/test_bookmark_shared_view_performance.py
+++ b/bookmarks/tests/test_bookmark_shared_view_performance.py
@@ -28,7 +28,7 @@ class BookmarkSharedViewPerformanceTestCase(TransactionTestCase, BookmarkFactory
context = CaptureQueriesContext(self.get_connection())
with context:
response = self.client.get(reverse('bookmarks:shared'))
- self.assertContains(response, 'data-is-bookmark-item', num_initial_bookmarks)
+ self.assertContains(response, ' ', num_initial_bookmarks)
number_of_queries = context.final_queries
@@ -41,4 +41,4 @@ class BookmarkSharedViewPerformanceTestCase(TransactionTestCase, BookmarkFactory
# assert num queries doesn't increase
with self.assertNumQueries(number_of_queries):
response = self.client.get(reverse('bookmarks:shared'))
- self.assertContains(response, 'data-is-bookmark-item', num_initial_bookmarks + num_additional_bookmarks)
+ self.assertContains(response, ' ', num_initial_bookmarks + num_additional_bookmarks)
diff --git a/bookmarks/tests/test_bookmarks_list_tag.py b/bookmarks/tests/test_bookmarks_list_template.py
similarity index 75%
rename from bookmarks/tests/test_bookmarks_list_tag.py
rename to bookmarks/tests/test_bookmarks_list_template.py
index e213e0c..39b2f8f 100644
--- a/bookmarks/tests/test_bookmarks_list_tag.py
+++ b/bookmarks/tests/test_bookmarks_list_template.py
@@ -1,18 +1,20 @@
+from typing import Type
+
from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import AnonymousUser
-from django.core.paginator import Paginator
from django.http import HttpResponse
from django.template import Template, RequestContext
from django.test import TestCase, RequestFactory
from django.urls import reverse
from django.utils import timezone, formats
+from bookmarks.middlewares import UserProfileMiddleware
from bookmarks.models import Bookmark, UserProfile, User
from bookmarks.tests.helpers import BookmarkFactoryMixin
-from bookmarks.middlewares import UserProfileMiddleware
+from bookmarks.views.partials import contexts
-class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
+class BookmarkListTemplateTest(TestCase, BookmarkFactoryMixin):
def assertBookmarksLink(self, html: str, bookmark: Bookmark, link_target: str = '_blank'):
unread = bookmark.unread
@@ -60,7 +62,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
# Edit link
edit_url = reverse('bookmarks:edit', args=[bookmark.id])
self.assertInHTML(f'''
- Edit
+ Edit
''', html, count=count)
# Archive link
self.assertInHTML(f'''
@@ -69,8 +71,8 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
''', html, count=count)
# Delete link
self.assertInHTML(f'''
- Remove
+ Remove
''', html, count=count)
def assertShareInfo(self, html: str, bookmark: Bookmark):
@@ -138,36 +140,24 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
''', html, count=count)
- def render_template(self, bookmarks: [Bookmark], template: Template, url: str = '/test',
+ def render_template(self,
+ url='/bookmarks',
+ context_type: Type[contexts.BookmarkListContext] = contexts.ActiveBookmarkListContext,
user: User | AnonymousUser = None) -> str:
rf = RequestFactory()
request = rf.get(url)
request.user = user or self.get_or_create_test_user()
middleware = UserProfileMiddleware(lambda r: HttpResponse())
middleware(request)
- paginator = Paginator(bookmarks, 10)
- page = paginator.page(1)
- context = RequestContext(request, {'bookmarks': page, 'return_url': '/test'})
+ bookmark_list_context = context_type(request)
+ context = RequestContext(request, {'bookmark_list': bookmark_list_context})
+
+ template = Template(
+ "{% include 'bookmarks/bookmark_list.html' %}"
+ )
return template.render(context)
- def render_default_template(self, bookmarks: [Bookmark], url: str = '/test',
- user: User | AnonymousUser = None) -> str:
- template = Template(
- '{% load bookmarks %}'
- '{% bookmark_list bookmarks return_url %}'
- )
- return self.render_template(bookmarks, template, url, user)
-
- def render_template_with_link_target(self, bookmarks: [Bookmark], link_target: str) -> str:
- template = Template(
- f'''
- {{% load bookmarks %}}
- {{% bookmark_list bookmarks return_url '{link_target}' %}}
- '''
- )
- return self.render_template(bookmarks, template)
-
def setup_date_format_test(self, date_display_setting: str, web_archive_url: str = ''):
bookmark = self.setup_bookmark()
bookmark.date_added = timezone.now() - relativedelta(days=8)
@@ -180,7 +170,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
def test_should_respect_absolute_date_setting(self):
bookmark = self.setup_date_format_test(UserProfile.BOOKMARK_DATE_DISPLAY_ABSOLUTE)
- html = self.render_default_template([bookmark])
+ html = self.render_template()
formatted_date = formats.date_format(bookmark.date_added, 'SHORT_DATE_FORMAT')
self.assertDateLabel(html, formatted_date)
@@ -188,45 +178,38 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
def test_should_render_web_archive_link_with_absolute_date_setting(self):
bookmark = self.setup_date_format_test(UserProfile.BOOKMARK_DATE_DISPLAY_ABSOLUTE,
'https://web.archive.org/web/20210811214511/https://wanikani.com/')
- html = self.render_default_template([bookmark])
+ html = self.render_template()
formatted_date = formats.date_format(bookmark.date_added, 'SHORT_DATE_FORMAT')
self.assertWebArchiveLink(html, formatted_date, bookmark.web_archive_snapshot_url)
def test_should_respect_relative_date_setting(self):
- bookmark = self.setup_date_format_test(UserProfile.BOOKMARK_DATE_DISPLAY_RELATIVE)
- html = self.render_default_template([bookmark])
+ self.setup_date_format_test(UserProfile.BOOKMARK_DATE_DISPLAY_RELATIVE)
+ html = self.render_template()
self.assertDateLabel(html, '1 week ago')
def test_should_render_web_archive_link_with_relative_date_setting(self):
bookmark = self.setup_date_format_test(UserProfile.BOOKMARK_DATE_DISPLAY_RELATIVE,
'https://web.archive.org/web/20210811214511/https://wanikani.com/')
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertWebArchiveLink(html, '1 week ago', bookmark.web_archive_snapshot_url)
def test_bookmark_link_target_should_be_blank_by_default(self):
bookmark = self.setup_bookmark()
-
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertBookmarksLink(html, bookmark, link_target='_blank')
- def test_bookmark_link_target_should_respect_link_target_parameter(self):
+ def test_bookmark_link_target_should_respect_user_profile(self):
+ profile = self.get_or_create_test_user().profile
+ profile.bookmark_link_target = UserProfile.BOOKMARK_LINK_TARGET_SELF
+ profile.save()
+
bookmark = self.setup_bookmark()
+ html = self.render_template()
- html = self.render_template_with_link_target([bookmark], '_self')
-
- self.assertBookmarksLink(html, bookmark, link_target='_self')
-
- def test_bookmark_link_target_should_respect_unread_flag(self):
- bookmark = self.setup_bookmark()
- html = self.render_template_with_link_target([bookmark], '_self')
- self.assertBookmarksLink(html, bookmark, link_target='_self')
-
- bookmark = self.setup_bookmark(unread=True)
- html = self.render_template_with_link_target([bookmark], '_self')
self.assertBookmarksLink(html, bookmark, link_target='_self')
def test_web_archive_link_target_should_be_blank_by_default(self):
@@ -235,39 +218,55 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
bookmark.web_archive_snapshot_url = 'https://example.com'
bookmark.save()
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertWebArchiveLink(html, '1 week ago', bookmark.web_archive_snapshot_url, link_target='_blank')
- def test_web_archive_link_target_respect_link_target_parameter(self):
+ def test_web_archive_link_target_should_respect_user_profile(self):
+ profile = self.get_or_create_test_user().profile
+ profile.bookmark_link_target = UserProfile.BOOKMARK_LINK_TARGET_SELF
+ profile.save()
+
bookmark = self.setup_bookmark()
bookmark.date_added = timezone.now() - relativedelta(days=8)
bookmark.web_archive_snapshot_url = 'https://example.com'
bookmark.save()
- html = self.render_template_with_link_target([bookmark], '_self')
+ html = self.render_template()
self.assertWebArchiveLink(html, '1 week ago', bookmark.web_archive_snapshot_url, link_target='_self')
+ def test_should_respect_unread_flag(self):
+ bookmark = self.setup_bookmark(unread=True)
+ html = self.render_template()
+
+ self.assertBookmarksLink(html, bookmark)
+
def test_show_bookmark_actions_for_owned_bookmarks(self):
bookmark = self.setup_bookmark()
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertBookmarkActions(html, bookmark)
self.assertNoShareInfo(html, bookmark)
def test_show_share_info_for_non_owned_bookmarks(self):
other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
- bookmark = self.setup_bookmark(user=other_user)
- html = self.render_default_template([bookmark])
+ other_user.profile.enable_sharing = True
+ other_user.profile.save()
+
+ bookmark = self.setup_bookmark(user=other_user, shared=True)
+ html = self.render_template(context_type=contexts.SharedBookmarkListContext)
self.assertNoBookmarkActions(html, bookmark)
self.assertShareInfo(html, bookmark)
def test_share_info_user_link_keeps_query_params(self):
other_user = User.objects.create_user('otheruser', 'otheruser@example.com', 'password123')
- bookmark = self.setup_bookmark(user=other_user)
- html = self.render_default_template([bookmark], url='/test?q=foo')
+ other_user.profile.enable_sharing = True
+ other_user.profile.save()
+
+ bookmark = self.setup_bookmark(user=other_user, shared=True, title='foo')
+ html = self.render_template(url='/bookmarks?q=foo', context_type=contexts.SharedBookmarkListContext)
self.assertInHTML(f'''
Shared by
@@ -281,7 +280,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
profile.save()
bookmark = self.setup_bookmark(favicon_file='https_example_com.png')
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertFaviconVisible(html, bookmark)
@@ -291,7 +290,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
profile.save()
bookmark = self.setup_bookmark(favicon_file='')
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertFaviconHidden(html, bookmark)
@@ -301,7 +300,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
profile.save()
bookmark = self.setup_bookmark(favicon_file='https_example_com.png')
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertFaviconHidden(html, bookmark)
@@ -310,7 +309,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
profile.save()
bookmark = self.setup_bookmark()
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertBookmarkURLHidden(html, bookmark)
@@ -320,7 +319,7 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
profile.save()
bookmark = self.setup_bookmark()
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertBookmarkURLVisible(html, bookmark)
@@ -330,68 +329,67 @@ class BookmarkListTagTest(TestCase, BookmarkFactoryMixin):
profile.save()
bookmark = self.setup_bookmark()
- html = self.render_default_template([bookmark])
+ html = self.render_template()
self.assertBookmarkURLHidden(html, bookmark)
def test_without_notes(self):
- bookmark = self.setup_bookmark()
- html = self.render_default_template([bookmark])
+ self.setup_bookmark()
+ html = self.render_template()
self.assertNotes(html, '', 0)
self.assertNotesToggle(html, 0)
def test_with_notes(self):
- bookmark = self.setup_bookmark(notes='Test note')
- html = self.render_default_template([bookmark])
+ self.setup_bookmark(notes='Test note')
+ html = self.render_template()
note_html = 'Test note
'
self.assertNotes(html, note_html, 1)
def test_note_renders_markdown(self):
- bookmark = self.setup_bookmark(notes='**Example:** `print("Hello world!")`')
- html = self.render_default_template([bookmark])
+ self.setup_bookmark(notes='**Example:** `print("Hello world!")`')
+ html = self.render_template()
note_html = 'Example: print("Hello world!")
'
self.assertNotes(html, note_html, 1)
def test_note_cleans_html(self):
- bookmark = self.setup_bookmark(notes='')
- html = self.render_default_template([bookmark])
+ self.setup_bookmark(notes='')
+ html = self.render_template()
note_html = '<script>alert("test")</script>'
self.assertNotes(html, note_html, 1)
def test_notes_are_hidden_initially_by_default(self):
- html = self.render_default_template([])
+ self.setup_bookmark(notes='Test note')
+ html = self.render_template()
- self.assertInHTML("""
-
- """, html)
+ self.assertIn('', html)
def test_notes_are_hidden_initially_with_permanent_notes_disabled(self):
profile = self.get_or_create_test_user().profile
profile.permanent_notes = False
profile.save()
- html = self.render_default_template([])
- self.assertInHTML("""
-
- """, html)
+ self.setup_bookmark(notes='Test note')
+ html = self.render_template()
+
+ self.assertIn('