from typing import Type from dateutil.relativedelta import relativedelta from django.contrib.auth.models import AnonymousUser 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.views.partials import contexts class BookmarkListTemplateTest(TestCase, BookmarkFactoryMixin): def assertBookmarksLink(self, html: str, bookmark: Bookmark, link_target: str = '_blank'): unread = bookmark.unread favicon_img = f'' if bookmark.favicon_file else '' self.assertInHTML( f''' {favicon_img} {bookmark.resolved_title} ''', html ) def assertDateLabel(self, html: str, label_content: str): self.assertInHTML(f''' {label_content} | ''', html) def assertWebArchiveLink(self, html: str, label_content: str, url: str, link_target: str = '_blank'): self.assertInHTML(f''' {label_content} | ''', html) def assertBookmarkActions(self, html: str, bookmark: Bookmark): self.assertBookmarkActionsCount(html, bookmark, count=1) def assertNoBookmarkActions(self, html: str, bookmark: Bookmark): self.assertBookmarkActionsCount(html, bookmark, count=0) def assertBookmarkActionsCount(self, html: str, bookmark: Bookmark, count=1): # Edit link edit_url = reverse('bookmarks:edit', args=[bookmark.id]) self.assertInHTML(f''' Edit ''', html, count=count) # Archive link self.assertInHTML(f''' ''', html, count=count) # Delete link self.assertInHTML(f''' ''', html, count=count) def assertShareInfo(self, html: str, bookmark: Bookmark): self.assertShareInfoCount(html, bookmark, 1) def assertNoShareInfo(self, html: str, bookmark: Bookmark): self.assertShareInfoCount(html, bookmark, 0) def assertShareInfoCount(self, html: str, bookmark: Bookmark, count=1): self.assertInHTML(f''' Shared by {bookmark.owner.username} ''', html, count=count) def assertFaviconVisible(self, html: str, bookmark: Bookmark): self.assertFaviconCount(html, bookmark, 1) def assertFaviconHidden(self, html: str, bookmark: Bookmark): self.assertFaviconCount(html, bookmark, 0) def assertFaviconCount(self, html: str, bookmark: Bookmark, count=1): self.assertInHTML(f''' ''', html, count=count) def assertBookmarkURLCount(self, html: str, bookmark: Bookmark, link_target: str = '_blank', count=0): self.assertInHTML(f'''
{bookmark.url}
''', html, count) def assertBookmarkURLVisible(self, html: str, bookmark: Bookmark): self.assertBookmarkURLCount(html, bookmark, count=1) def assertBookmarkURLHidden(self, html: str, bookmark: Bookmark, link_target: str = '_blank'): self.assertBookmarkURLCount(html, bookmark, count=0) def assertNotes(self, html: str, notes_html: str, count=1): self.assertInHTML(f'''
{notes_html}
''', html, count=count) def assertNotesToggle(self, html: str, count=1): self.assertInHTML(f''' ''', html, count=count) 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) 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 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) bookmark.web_archive_snapshot_url = web_archive_url bookmark.save() user = self.get_or_create_test_user() user.profile.bookmark_date_display = date_display_setting user.profile.save() return bookmark def test_should_respect_absolute_date_setting(self): bookmark = self.setup_date_format_test(UserProfile.BOOKMARK_DATE_DISPLAY_ABSOLUTE) html = self.render_template() formatted_date = formats.date_format(bookmark.date_added, 'SHORT_DATE_FORMAT') self.assertDateLabel(html, formatted_date) 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_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): 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_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_template() self.assertBookmarksLink(html, bookmark, link_target='_blank') 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() self.assertBookmarksLink(html, bookmark, link_target='_self') def test_web_archive_link_target_should_be_blank_by_default(self): 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() self.assertWebArchiveLink(html, '1 week ago', bookmark.web_archive_snapshot_url, link_target='_blank') 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() 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_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') 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') 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 {bookmark.owner.username} ''', html) def test_favicon_should_be_visible_when_favicons_enabled(self): profile = self.get_or_create_test_user().profile profile.enable_favicons = True profile.save() bookmark = self.setup_bookmark(favicon_file='https_example_com.png') html = self.render_template() self.assertFaviconVisible(html, bookmark) def test_favicon_should_be_hidden_when_there_is_no_icon(self): profile = self.get_or_create_test_user().profile profile.enable_favicons = True profile.save() bookmark = self.setup_bookmark(favicon_file='') html = self.render_template() self.assertFaviconHidden(html, bookmark) def test_favicon_should_be_hidden_when_favicons_disabled(self): profile = self.get_or_create_test_user().profile profile.enable_favicons = False profile.save() bookmark = self.setup_bookmark(favicon_file='https_example_com.png') html = self.render_template() self.assertFaviconHidden(html, bookmark) def test_bookmark_url_should_be_hidden_by_default(self): profile = self.get_or_create_test_user().profile profile.save() bookmark = self.setup_bookmark() html = self.render_template() self.assertBookmarkURLHidden(html, bookmark) def test_show_bookmark_url_when_enabled(self): profile = self.get_or_create_test_user().profile profile.display_url = True profile.save() bookmark = self.setup_bookmark() html = self.render_template() self.assertBookmarkURLVisible(html, bookmark) def test_hide_bookmark_url_when_disabled(self): profile = self.get_or_create_test_user().profile profile.display_url = False profile.save() bookmark = self.setup_bookmark() html = self.render_template() self.assertBookmarkURLHidden(html, bookmark) def test_without_notes(self): self.setup_bookmark() html = self.render_template() self.assertNotes(html, '', 0) self.assertNotesToggle(html, 0) def test_with_notes(self): 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): 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): 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): self.setup_bookmark(notes='Test note') html = self.render_template() self.assertIn('