mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-13 05:29:26 +02:00
Allow bulk editing unread and shared state of bookmarks (#517)
* Move bulk actions into select * Update tests * Implement bulk read / unread actions * Implement bulk share/unshare actions * Show correct archiving actions * Allow selecting bookmarks across pages * Dynamically update select across checkbox * Filter available bulk actions * Refactor tag autocomplete toggling
This commit is contained in:
232
bookmarks/e2e/e2e_test_bookmark_page_bulk_edit.py
Normal file
232
bookmarks/e2e/e2e_test_bookmark_page_bulk_edit.py
Normal file
@@ -0,0 +1,232 @@
|
||||
from django.urls import reverse
|
||||
from playwright.sync_api import sync_playwright, expect
|
||||
|
||||
from bookmarks.e2e.helpers import LinkdingE2ETestCase
|
||||
from bookmarks.models import Bookmark
|
||||
|
||||
|
||||
class BookmarkPagePartialUpdatesE2ETestCase(LinkdingE2ETestCase):
|
||||
def setup_test_data(self):
|
||||
self.setup_numbered_bookmarks(50)
|
||||
self.setup_numbered_bookmarks(50, archived=True)
|
||||
self.setup_numbered_bookmarks(50, prefix='foo')
|
||||
self.setup_numbered_bookmarks(50, archived=True, prefix='foo')
|
||||
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='Archived Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='foo').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='foo').count())
|
||||
|
||||
def test_active_bookmarks_bulk_select_across(self):
|
||||
self.setup_test_data()
|
||||
|
||||
with sync_playwright() as p:
|
||||
self.open(reverse('bookmarks:index'), p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertEqual(0, Bookmark.objects.filter(is_archived=False, title__startswith='Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='Archived Bookmark').count())
|
||||
self.assertEqual(0, Bookmark.objects.filter(is_archived=False, title__startswith='foo').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='foo').count())
|
||||
|
||||
def test_archived_bookmarks_bulk_select_across(self):
|
||||
self.setup_test_data()
|
||||
|
||||
with sync_playwright() as p:
|
||||
self.open(reverse('bookmarks:archived'), p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='Bookmark').count())
|
||||
self.assertEqual(0, Bookmark.objects.filter(is_archived=True, title__startswith='Archived Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='foo').count())
|
||||
self.assertEqual(0, Bookmark.objects.filter(is_archived=True, title__startswith='foo').count())
|
||||
|
||||
def test_active_bookmarks_bulk_select_across_respects_query(self):
|
||||
self.setup_test_data()
|
||||
|
||||
with sync_playwright() as p:
|
||||
self.open(reverse('bookmarks:index') + '?q=foo', p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='Archived Bookmark').count())
|
||||
self.assertEqual(0, Bookmark.objects.filter(is_archived=False, title__startswith='foo').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='foo').count())
|
||||
|
||||
def test_archived_bookmarks_bulk_select_across_respects_query(self):
|
||||
self.setup_test_data()
|
||||
|
||||
with sync_playwright() as p:
|
||||
self.open(reverse('bookmarks:archived') + '?q=foo', p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=True, title__startswith='Archived Bookmark').count())
|
||||
self.assertEqual(50, Bookmark.objects.filter(is_archived=False, title__startswith='foo').count())
|
||||
self.assertEqual(0, Bookmark.objects.filter(is_archived=True, title__startswith='foo').count())
|
||||
|
||||
def test_select_all_toggles_all_checkboxes(self):
|
||||
self.setup_numbered_bookmarks(5)
|
||||
|
||||
with sync_playwright() as p:
|
||||
url = reverse('bookmarks:index')
|
||||
page = self.open(url, p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
|
||||
checkboxes = page.locator('label[ld-bulk-edit-checkbox] input')
|
||||
self.assertEqual(6, checkboxes.count())
|
||||
for i in range(checkboxes.count()):
|
||||
expect(checkboxes.nth(i)).not_to_be_checked()
|
||||
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
|
||||
for i in range(checkboxes.count()):
|
||||
expect(checkboxes.nth(i)).to_be_checked()
|
||||
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
|
||||
for i in range(checkboxes.count()):
|
||||
expect(checkboxes.nth(i)).not_to_be_checked()
|
||||
|
||||
def test_select_all_shows_select_across(self):
|
||||
self.setup_numbered_bookmarks(5)
|
||||
|
||||
with sync_playwright() as p:
|
||||
url = reverse('bookmarks:index')
|
||||
self.open(url, p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_visible()
|
||||
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
expect(self.locate_bulk_edit_select_across()).to_be_visible()
|
||||
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_visible()
|
||||
|
||||
def test_select_across_is_unchecked_when_toggling_all(self):
|
||||
self.setup_numbered_bookmarks(5)
|
||||
|
||||
with sync_playwright() as p:
|
||||
url = reverse('bookmarks:index')
|
||||
self.open(url, p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
|
||||
# Show select across, check it
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
expect(self.locate_bulk_edit_select_across()).to_be_checked()
|
||||
|
||||
# Hide select across by toggling select all
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_visible()
|
||||
|
||||
# Show select across again, verify it is unchecked
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_checked()
|
||||
|
||||
def test_select_across_is_unchecked_when_toggling_bookmark(self):
|
||||
self.setup_numbered_bookmarks(5)
|
||||
|
||||
with sync_playwright() as p:
|
||||
url = reverse('bookmarks:index')
|
||||
self.open(url, p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
|
||||
# Show select across, check it
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
expect(self.locate_bulk_edit_select_across()).to_be_checked()
|
||||
|
||||
# Hide select across by toggling a single bookmark
|
||||
self.locate_bookmark('Bookmark 1').locator('label[ld-bulk-edit-checkbox]').click()
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_visible()
|
||||
|
||||
# Show select across again, verify it is unchecked
|
||||
self.locate_bookmark('Bookmark 1').locator('label[ld-bulk-edit-checkbox]').click()
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_checked()
|
||||
|
||||
def test_execute_resets_all_checkboxes(self):
|
||||
self.setup_numbered_bookmarks(100)
|
||||
|
||||
with sync_playwright() as p:
|
||||
url = reverse('bookmarks:index')
|
||||
page = self.open(url, p)
|
||||
|
||||
# Select all bookmarks, enable select across
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
self.locate_bulk_edit_select_across().click()
|
||||
|
||||
# Get reference for bookmark list
|
||||
bookmark_list = page.locator('ul[ld-bookmark-list]')
|
||||
|
||||
# Execute bulk action
|
||||
self.select_bulk_action('Mark as unread')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
# Wait until bookmark list is updated (old reference becomes invisible)
|
||||
expect(bookmark_list).not_to_be_visible()
|
||||
|
||||
# Verify bulk edit checkboxes are reset
|
||||
checkboxes = page.locator('label[ld-bulk-edit-checkbox] input')
|
||||
self.assertEqual(31, checkboxes.count())
|
||||
for i in range(checkboxes.count()):
|
||||
expect(checkboxes.nth(i)).not_to_be_checked()
|
||||
|
||||
# Toggle select all and verify select across is reset
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
expect(self.locate_bulk_edit_select_across()).not_to_be_checked()
|
||||
|
||||
def test_update_select_across_bookmark_count(self):
|
||||
self.setup_numbered_bookmarks(100)
|
||||
|
||||
with sync_playwright() as p:
|
||||
url = reverse('bookmarks:index')
|
||||
self.open(url, p)
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
|
||||
expect(self.locate_bulk_edit_bar().get_by_text('All pages (100 bookmarks)')).to_be_visible()
|
||||
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.locate_bulk_edit_select_all().click()
|
||||
|
||||
expect(self.locate_bulk_edit_bar().get_by_text('All pages (70 bookmarks)')).to_be_visible()
|
@@ -150,7 +150,8 @@ class BookmarkPagePartialUpdatesE2ETestCase(LinkdingE2ETestCase):
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bookmark('Bookmark 2').locator('label[ld-bulk-edit-checkbox]').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Archive').click()
|
||||
self.select_bulk_action('Archive')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertVisibleBookmarks(['Bookmark 1', 'Bookmark 3'])
|
||||
@@ -165,7 +166,8 @@ class BookmarkPagePartialUpdatesE2ETestCase(LinkdingE2ETestCase):
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bookmark('Bookmark 2').locator('label[ld-bulk-edit-checkbox]').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Delete').click()
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertVisibleBookmarks(['Bookmark 1', 'Bookmark 3'])
|
||||
@@ -197,7 +199,7 @@ class BookmarkPagePartialUpdatesE2ETestCase(LinkdingE2ETestCase):
|
||||
self.assertVisibleTags(['Archived Tag 1', 'Archived Tag 3'])
|
||||
self.assertReloads(0)
|
||||
|
||||
def test_archived_bookmarks_partial_update_on_bulk_archive(self):
|
||||
def test_archived_bookmarks_partial_update_on_bulk_unarchive(self):
|
||||
self.setup_fixture()
|
||||
|
||||
with sync_playwright() as p:
|
||||
@@ -205,7 +207,8 @@ class BookmarkPagePartialUpdatesE2ETestCase(LinkdingE2ETestCase):
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bookmark('Archived Bookmark 2').locator('label[ld-bulk-edit-checkbox]').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Archive').click()
|
||||
self.select_bulk_action('Unarchive')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertVisibleBookmarks(['Archived Bookmark 1', 'Archived Bookmark 3'])
|
||||
@@ -220,7 +223,8 @@ class BookmarkPagePartialUpdatesE2ETestCase(LinkdingE2ETestCase):
|
||||
|
||||
self.locate_bulk_edit_toggle().click()
|
||||
self.locate_bookmark('Archived Bookmark 2').locator('label[ld-bulk-edit-checkbox]').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Delete').click()
|
||||
self.select_bulk_action('Delete')
|
||||
self.locate_bulk_edit_bar().get_by_text('Execute').click()
|
||||
self.locate_bulk_edit_bar().get_by_text('Confirm').click()
|
||||
|
||||
self.assertVisibleBookmarks(['Archived Bookmark 1', 'Archived Bookmark 3'])
|
||||
|
@@ -41,5 +41,14 @@ class LinkdingE2ETestCase(LiveServerTestCase, BookmarkFactoryMixin):
|
||||
def locate_bulk_edit_bar(self):
|
||||
return self.page.locator('.bulk-edit-bar')
|
||||
|
||||
def locate_bulk_edit_select_all(self):
|
||||
return self.locate_bulk_edit_bar().locator('label[ld-bulk-edit-checkbox][all]')
|
||||
|
||||
def locate_bulk_edit_select_across(self):
|
||||
return self.locate_bulk_edit_bar().locator('label.select-across')
|
||||
|
||||
def locate_bulk_edit_toggle(self):
|
||||
return self.page.get_by_title('Bulk edit')
|
||||
|
||||
def select_bulk_action(self, value: str):
|
||||
return self.locate_bulk_edit_bar().locator('select[name="bulk_action"]').select_option(value)
|
||||
|
Reference in New Issue
Block a user