mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-21 19:03:02 +00:00
Support Open Graph description (#602)
* Support pytest for running tests * Support extracting description from meta og:description property * Revert changes to TOC * Add test --------- Co-authored-by: Sascha Ißbrücker <sascha.issbruecker@gmail.com>
This commit is contained in:
parent
81ae55bc1c
commit
150dfecc6f
6 changed files with 53 additions and 19 deletions
17
README.md
17
README.md
|
@ -9,11 +9,11 @@
|
||||||
## Overview
|
## Overview
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Using Docker](#using-docker)
|
- [Using Docker](#using-docker)
|
||||||
- [Using Docker Compose](#using-docker-compose)
|
- [Using Docker Compose](#using-docker-compose)
|
||||||
- [User Setup](#user-setup)
|
- [User Setup](#user-setup)
|
||||||
- [Reverse Proxy Setup](#reverse-proxy-setup)
|
- [Reverse Proxy Setup](#reverse-proxy-setup)
|
||||||
- [Managed Hosting Options](#managed-hosting-options)
|
- [Managed Hosting Options](#managed-hosting-options)
|
||||||
- [Documentation](#documentation)
|
- [Documentation](#documentation)
|
||||||
- [Browser Extension](#browser-extension)
|
- [Browser Extension](#browser-extension)
|
||||||
- [Community](#community)
|
- [Community](#community)
|
||||||
|
@ -103,7 +103,7 @@ docker-compose up -d
|
||||||
|
|
||||||
To complete the setup, you still have to [create an initial user](#user-setup), so that you can access your installation.
|
To complete the setup, you still have to [create an initial user](#user-setup), so that you can access your installation.
|
||||||
|
|
||||||
### User setup
|
### User Setup
|
||||||
|
|
||||||
For security reasons, the linkding Docker image does not provide an initial user, so you have to create one after setting up an installation. To do so, replace the credentials in the following command and run it:
|
For security reasons, the linkding Docker image does not provide an initial user, so you have to create one after setting up an installation. To do so, replace the credentials in the following command and run it:
|
||||||
|
|
||||||
|
@ -300,3 +300,8 @@ Start the Django development server with:
|
||||||
python3 manage.py runserver
|
python3 manage.py runserver
|
||||||
```
|
```
|
||||||
The frontend is now available under http://localhost:8000
|
The frontend is now available under http://localhost:8000
|
||||||
|
|
||||||
|
Run all tests with pytest
|
||||||
|
```
|
||||||
|
pytest
|
||||||
|
```
|
||||||
|
|
|
@ -41,8 +41,13 @@ def load_website_metadata(url: str):
|
||||||
|
|
||||||
title = soup.title.string.strip() if soup.title is not None else None
|
title = soup.title.string.strip() if soup.title is not None else None
|
||||||
description_tag = soup.find('meta', attrs={'name': 'description'})
|
description_tag = soup.find('meta', attrs={'name': 'description'})
|
||||||
description = description = description_tag['content'].strip() if description_tag and description_tag[
|
description = description_tag['content'].strip() if description_tag and description_tag[
|
||||||
'content'] else None
|
'content'] else None
|
||||||
|
|
||||||
|
if not description:
|
||||||
|
description_tag = soup.find('meta', attrs={'property': 'og:description'})
|
||||||
|
description = description_tag['content'].strip() if description_tag and description_tag['content'] else None
|
||||||
|
|
||||||
end = timezone.now()
|
end = timezone.now()
|
||||||
logger.debug(f'Parsing duration: {end - start}')
|
logger.debug(f'Parsing duration: {end - start}')
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -183,9 +183,8 @@ class BookmarkNewViewTestCase(TestCase, BookmarkFactoryMixin):
|
||||||
</div>
|
</div>
|
||||||
''', html)
|
''', html)
|
||||||
|
|
||||||
|
def test_should_hide_notes_if_there_are_no_notes(self):
|
||||||
|
bookmark = self.setup_bookmark()
|
||||||
|
response = self.client.get(reverse('bookmarks:edit', args=[bookmark.id]))
|
||||||
|
|
||||||
def test_should_hide_notes_if_there_are_no_notes(self):
|
self.assertContains(response, '<details class="notes">', count=1)
|
||||||
bookmark = self.setup_bookmark()
|
|
||||||
response = self.client.get(reverse('bookmarks:edit', args=[bookmark.id]))
|
|
||||||
|
|
||||||
self.assertContains(response, '<details class="notes">', count=1)
|
|
||||||
|
|
|
@ -29,14 +29,17 @@ class WebsiteLoaderTestCase(TestCase):
|
||||||
# clear cached metadata before test run
|
# clear cached metadata before test run
|
||||||
website_loader.load_website_metadata.cache_clear()
|
website_loader.load_website_metadata.cache_clear()
|
||||||
|
|
||||||
def render_html_document(self, title, description):
|
def render_html_document(self, title, description='', og_description=''):
|
||||||
|
meta_description = f'<meta name="description" content="{description}">' if description else ''
|
||||||
|
meta_og_description = f'<meta property="og:description" content="{og_description}">' if og_description else ''
|
||||||
return f'''
|
return f'''
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="description" content="{description}">
|
{meta_description}
|
||||||
|
{meta_og_description}
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body></body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -94,3 +97,19 @@ class WebsiteLoaderTestCase(TestCase):
|
||||||
metadata = website_loader.load_website_metadata('https://example.com')
|
metadata = website_loader.load_website_metadata('https://example.com')
|
||||||
self.assertEqual('test title', metadata.title)
|
self.assertEqual('test title', metadata.title)
|
||||||
self.assertEqual('test description', metadata.description)
|
self.assertEqual('test description', metadata.description)
|
||||||
|
|
||||||
|
def test_load_website_metadata_using_og_description(self):
|
||||||
|
with mock.patch('bookmarks.services.website_loader.load_page') as mock_load_page:
|
||||||
|
mock_load_page.return_value = self.render_html_document('test title', '',
|
||||||
|
og_description='test og description')
|
||||||
|
metadata = website_loader.load_website_metadata('https://example.com')
|
||||||
|
self.assertEqual('test title', metadata.title)
|
||||||
|
self.assertEqual('test og description', metadata.description)
|
||||||
|
|
||||||
|
def test_load_website_metadata_prefers_description_over_og_description(self):
|
||||||
|
with mock.patch('bookmarks.services.website_loader.load_page') as mock_load_page:
|
||||||
|
mock_load_page.return_value = self.render_html_document('test title', 'test description',
|
||||||
|
og_description='test og description')
|
||||||
|
metadata = website_loader.load_website_metadata('https://example.com')
|
||||||
|
self.assertEqual('test title', metadata.title)
|
||||||
|
self.assertEqual('test description', metadata.description)
|
||||||
|
|
4
pytest.ini
Normal file
4
pytest.ini
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[pytest]
|
||||||
|
DJANGO_SETTINGS_MODULE = siteroot.settings.dev
|
||||||
|
# -- recommended but optional:
|
||||||
|
python_files = tests.py test_*.py *_tests.py
|
|
@ -1,13 +1,12 @@
|
||||||
asgiref==3.5.2
|
asgiref==3.5.2
|
||||||
beautifulsoup4==4.11.1
|
beautifulsoup4==4.11.1
|
||||||
bleach==6.0.0
|
|
||||||
bleach-allowlist==1.0.3
|
bleach-allowlist==1.0.3
|
||||||
|
bleach==6.0.0
|
||||||
certifi==2023.7.22
|
certifi==2023.7.22
|
||||||
charset-normalizer==2.1.1
|
charset-normalizer==2.1.1
|
||||||
click==8.1.3
|
click==8.1.3
|
||||||
confusable-homoglyphs==3.2.0
|
confusable-homoglyphs==3.2.0
|
||||||
coverage==5.5
|
coverage==5.5
|
||||||
Django==4.1.13
|
|
||||||
django-appconf==1.0.5
|
django-appconf==1.0.5
|
||||||
django-compressor==4.1
|
django-compressor==4.1
|
||||||
django-debug-toolbar==3.6.0
|
django-debug-toolbar==3.6.0
|
||||||
|
@ -15,6 +14,7 @@ django-generate-secret-key==1.0.2
|
||||||
django-registration==3.3
|
django-registration==3.3
|
||||||
django-sass-processor==1.2.1
|
django-sass-processor==1.2.1
|
||||||
django-widget-tweaks==1.4.12
|
django-widget-tweaks==1.4.12
|
||||||
|
Django==4.1.13
|
||||||
django4-background-tasks==1.2.7
|
django4-background-tasks==1.2.7
|
||||||
djangorestframework==3.13.1
|
djangorestframework==3.13.1
|
||||||
greenlet==3.0.1
|
greenlet==3.0.1
|
||||||
|
@ -24,6 +24,8 @@ Markdown==3.4.3
|
||||||
playwright==1.40.0
|
playwright==1.40.0
|
||||||
psycopg2-binary==2.9.5
|
psycopg2-binary==2.9.5
|
||||||
pyee==11.0.1
|
pyee==11.0.1
|
||||||
|
pytest-django==4.7.0
|
||||||
|
pytest==7.4.4
|
||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
pytz==2022.2.1
|
pytz==2022.2.1
|
||||||
rcssmin==1.1.0
|
rcssmin==1.1.0
|
||||||
|
|
Loading…
Reference in a new issue