diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index ded1afe..d80fc61 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -3,22 +3,45 @@ name: linkding CI on: [push] jobs: - run_tests: - name: Run Django Tests + unit_tests: + name: Unit Tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v4 with: python-version: "3.10" - name: Set up Node - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: - node-version: 14 - - name: Install Python dependencies - run: pip install -r requirements.txt + node-version: 18 - name: Install Node dependencies run: npm install + - name: Setup Python environment + run: pip install -r requirements.txt - name: Run tests - run: python manage.py test + run: python manage.py test bookmarks.tests + e2e_tests: + name: E2E Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install Node dependencies + run: npm install + - name: Setup Python environment + run: | + pip install -r requirements.txt + playwright install chromium + python manage.py compilescss + python manage.py collectstatic --ignore=*.scss + - name: Run tests + run: python manage.py test bookmarks.e2e diff --git a/bookmarks/e2e/__init__.py b/bookmarks/e2e/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bookmarks/e2e/helpers.py b/bookmarks/e2e/helpers.py new file mode 100644 index 0000000..7a7116f --- /dev/null +++ b/bookmarks/e2e/helpers.py @@ -0,0 +1,21 @@ +from django.contrib.staticfiles.testing import LiveServerTestCase +from playwright.sync_api import BrowserContext + +from bookmarks.tests.helpers import BookmarkFactoryMixin + + +class LinkdingE2ETestCase(LiveServerTestCase, BookmarkFactoryMixin): + def setUp(self) -> None: + self.client.force_login(self.get_or_create_test_user()) + self.cookie = self.client.cookies['sessionid'] + + def setup_browser(self, playwright) -> BrowserContext: + browser = playwright.chromium.launch(headless=True) + context = browser.new_context() + context.add_cookies([{ + 'name': 'sessionid', + 'value': self.cookie.value, + 'domain': self.live_server_url.replace('http:', ''), + 'path': '/' + }]) + return context diff --git a/bookmarks/e2e/test_bookmark_form.py b/bookmarks/e2e/test_bookmark_form.py new file mode 100644 index 0000000..32f9913 --- /dev/null +++ b/bookmarks/e2e/test_bookmark_form.py @@ -0,0 +1,51 @@ +from django.urls import reverse +from playwright.sync_api import sync_playwright + +from bookmarks.e2e.helpers import LinkdingE2ETestCase + + +class BookmarkFormE2ETestCase(LinkdingE2ETestCase): + def test_create_should_check_for_existing_bookmark(self): + existing_bookmark = self.setup_bookmark(title='Existing title', + description='Existing description', + tags=[self.setup_tag(name='tag1'), self.setup_tag(name='tag2')], + website_title='Existing website title', + website_description='Existing website description', + unread=True) + tag_names = ' '.join(existing_bookmark.tag_names) + + with sync_playwright() as p: + browser = self.setup_browser(p) + page = browser.new_page() + page.goto(self.live_server_url + reverse('bookmarks:new')) + + # Enter bookmarked URL + page.get_by_label('URL').fill(existing_bookmark.url) + # Already bookmarked hint should be visible + page.get_by_text('This URL is already bookmarked.').wait_for(timeout=2000) + # Form should be pre-filled with data from existing bookmark + self.assertEqual(existing_bookmark.title, page.get_by_label('Title').input_value()) + self.assertEqual(existing_bookmark.description, page.get_by_label('Description').input_value()) + self.assertEqual(existing_bookmark.website_title, page.get_by_label('Title').get_attribute('placeholder')) + self.assertEqual(existing_bookmark.website_description, + page.get_by_label('Description').get_attribute('placeholder')) + self.assertEqual(tag_names, page.get_by_label('Tags').input_value()) + self.assertTrue(tag_names, page.get_by_label('Mark as unread').is_checked()) + + # Enter non-bookmarked URL + page.get_by_label('URL').fill('https://example.com/unknown') + # Already bookmarked hint should be hidden + page.get_by_text('This URL is already bookmarked.').wait_for(state='hidden', timeout=2000) + + browser.close() + + def test_edit_should_not_check_for_existing_bookmark(self): + bookmark = self.setup_bookmark() + + with sync_playwright() as p: + browser = self.setup_browser(p) + page = browser.new_page() + page.goto(self.live_server_url + reverse('bookmarks:edit', args=[bookmark.id])) + + page.wait_for_timeout(timeout=1000) + page.get_by_text('This URL is already bookmarked.').wait_for(state='hidden') diff --git a/bookmarks/e2e/test_global_shortcuts.py b/bookmarks/e2e/test_global_shortcuts.py new file mode 100644 index 0000000..1d60b45 --- /dev/null +++ b/bookmarks/e2e/test_global_shortcuts.py @@ -0,0 +1,30 @@ +from django.urls import reverse +from playwright.sync_api import sync_playwright, expect + +from bookmarks.e2e.helpers import LinkdingE2ETestCase + + +class GlobalShortcutsE2ETestCase(LinkdingE2ETestCase): + def test_focus_search(self): + with sync_playwright() as p: + browser = self.setup_browser(p) + page = browser.new_page() + page.goto(self.live_server_url + reverse('bookmarks:index')) + + page.press('body', 's') + + expect(page.get_by_placeholder('Search for words or #tags')).to_be_focused() + + browser.close() + + def test_add_bookmark(self): + with sync_playwright() as p: + browser = self.setup_browser(p) + page = browser.new_page() + page.goto(self.live_server_url + reverse('bookmarks:index')) + + page.press('body', 'n') + + expect(page).to_have_url(self.live_server_url + reverse('bookmarks:new')) + + browser.close() diff --git a/requirements.txt b/requirements.txt index 9a0ff04..4aa9890 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,9 +15,12 @@ django-sass-processor==1.2.1 django-widget-tweaks==1.4.12 django4-background-tasks==1.2.7 djangorestframework==3.13.1 +greenlet==2.0.1 idna==3.3 libsass==0.21.0 +playwright==1.29.1 psycopg2-binary==2.9.5 +pyee==9.0.4 python-dateutil==2.8.2 pytz==2022.2.1 rcssmin==1.1.0