326 lines
12 KiB
Python
326 lines
12 KiB
Python
import base64
|
|
from html.parser import HTMLParser
|
|
from io import BytesIO
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.conf import settings
|
|
from django.core.files import File
|
|
from django.template import Context, Template
|
|
from django.test import override_settings
|
|
from django.test.client import RequestFactory
|
|
from django.urls import reverse
|
|
from webtest import Upload
|
|
|
|
from combo.apps.notifications.models import Notification
|
|
from combo.apps.pwa.models import PushSubscription, PwaNavigationEntry, PwaSettings
|
|
from combo.data.models import Page
|
|
|
|
from .test_manager import login
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_manifest_json(app):
|
|
assert app.get('/manifest.json', status=200).json['name'] == 'Compte Citoyen'
|
|
with override_settings(TEMPLATE_VARS={'global_title': 'FooBar'}):
|
|
assert app.get('/manifest.json', status=200).json['name'] == 'FooBar'
|
|
|
|
|
|
def test_service_worker(app):
|
|
app.get('/service-worker.js', status=200)
|
|
resp = app.get('/service-worker-registration.js', status=200)
|
|
assert 'applicationServerPublicKey = null' in resp.text
|
|
|
|
pwa_settings = PwaSettings.singleton()
|
|
pwa_settings.push_notifications = True
|
|
pwa_settings.save()
|
|
|
|
resp = app.get('/service-worker-registration.js', status=200)
|
|
assert 'applicationServerPublicKey = "' in resp.text
|
|
|
|
# check legacy settings are still supported
|
|
with override_settings(
|
|
PWA_VAPID_PUBLIK_KEY="BFzvUdXB...",
|
|
PWA_VAPID_PRIVATE_KEY="4WbCnBF...",
|
|
PWA_VAPID_CLAIMS={'sub': 'mailto:admin@entrouvert.com'},
|
|
):
|
|
resp = app.get('/service-worker-registration.js', status=200)
|
|
assert 'applicationServerPublicKey = "BFzvUdXB..."' in resp.text
|
|
|
|
|
|
def test_webpush_subscription(app, john_doe, jane_doe):
|
|
app.post_json(reverse('pwa-subscribe-push'), params={'sample': 'content'}, status=403)
|
|
app.get(reverse('pwa-subscribe-push'), status=403)
|
|
app = login(app, john_doe.username, john_doe.username)
|
|
app.post_json(reverse('pwa-subscribe-push'), params={'sample': 'content'}, status=200)
|
|
assert PushSubscription.objects.count() == 1
|
|
app.post_json(reverse('pwa-subscribe-push'), params={'sample': 'content2'}, status=200)
|
|
assert PushSubscription.objects.count() == 2
|
|
|
|
app = login(app, jane_doe.username, jane_doe.username)
|
|
app.post_json(reverse('pwa-subscribe-push'), params={'sample': 'content'}, status=200)
|
|
assert PushSubscription.objects.count() == 3
|
|
|
|
app = login(app, john_doe.username, john_doe.username)
|
|
app.post_json(reverse('pwa-subscribe-push'), params=None, status=200)
|
|
assert PushSubscription.objects.count() == 1
|
|
|
|
app = login(app, john_doe.username, john_doe.username)
|
|
resp = app.post(reverse('pwa-subscribe-push'), params='', status=400)
|
|
assert 'bad json' in resp.text
|
|
|
|
|
|
def test_webpush_notification(app, john_doe):
|
|
PushSubscription.objects.all().delete()
|
|
app = login(app, john_doe.username, john_doe.username)
|
|
app.post_json(reverse('pwa-subscribe-push'), params={'sample': 'content'}, status=200)
|
|
|
|
pwa_settings = PwaSettings.singleton()
|
|
pwa_settings.push_notifications = False
|
|
pwa_settings.save()
|
|
with mock.patch('pywebpush.webpush') as webpush:
|
|
Notification.notify(john_doe, 'test', body='hello world')
|
|
assert webpush.call_count == 0
|
|
|
|
pwa_settings.push_notifications = True
|
|
pwa_settings.save()
|
|
with mock.patch('pywebpush.webpush') as webpush:
|
|
Notification.notify(john_doe, 'test', body='hello world')
|
|
assert webpush.call_count == 1
|
|
assert webpush.call_args[1]['subscription_info'] == {'sample': 'content'}
|
|
|
|
# check legacy settings are still supported
|
|
with override_settings(
|
|
PWA_VAPID_PUBLIK_KEY="BFzvUdXB...",
|
|
PWA_VAPID_PRIVATE_KEY="4WbCnBF...",
|
|
PWA_VAPID_CLAIMS={'sub': 'mailto:admin@entrouvert.com'},
|
|
):
|
|
with mock.patch('pywebpush.webpush') as webpush:
|
|
Notification.notify(john_doe, 'test', body='hello world')
|
|
assert webpush.call_count == 1
|
|
assert webpush.call_args[1]['subscription_info'] == {'sample': 'content'}
|
|
assert webpush.call_args[1]['vapid_private_key'] == settings.PWA_VAPID_PRIVATE_KEY
|
|
assert webpush.call_args[1]['vapid_claims'] == settings.PWA_VAPID_CLAIMS
|
|
|
|
|
|
def test_no_pwa_manager(app, admin_user):
|
|
app = login(app)
|
|
resp = app.get('/manage/', status=200)
|
|
assert not '/manage/pwa/' in resp.text
|
|
|
|
|
|
def test_pwa_manager(app, admin_user):
|
|
app = login(app)
|
|
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}):
|
|
resp = app.get('/manage/', status=200)
|
|
assert '/manage/pwa/' in resp.text
|
|
resp = app.get('/manage/pwa/')
|
|
resp.form['offline_text'] = 'You are offline.'
|
|
assert resp.form['offline_retry_button'].checked
|
|
resp.form['offline_retry_button'].checked = False
|
|
resp = resp.form.submit().follow()
|
|
assert resp.form['offline_text'].value == 'You are offline.'
|
|
assert resp.form['offline_retry_button'].checked is False
|
|
|
|
resp = app.get('/manage/pwa/')
|
|
resp = resp.click('Add a navigation entry')
|
|
resp.form['label'] = 'Hello'
|
|
resp.form['url'] = 'https://www.example.net'
|
|
resp = resp.form.submit().follow()
|
|
assert PwaNavigationEntry.objects.all().count() == 1
|
|
|
|
page = Page(title='test', slug='test')
|
|
page.save()
|
|
|
|
resp = resp.click('Add a navigation entry')
|
|
resp.form['link_page'] = page.id
|
|
resp = resp.form.submit().follow()
|
|
assert PwaNavigationEntry.objects.all().count() == 2
|
|
|
|
for i in range(3):
|
|
resp = resp.click('Add a navigation entry')
|
|
resp.form['label'] = 'Hello %s' % i
|
|
resp.form['url'] = 'https://www.example.net'
|
|
resp = resp.form.submit().follow()
|
|
|
|
# max 5 items
|
|
assert 'Add a navigation entry' not in resp.text
|
|
|
|
# reorder items, reverse them all
|
|
entries = PwaNavigationEntry.objects.all()
|
|
app.get(
|
|
'/manage/pwa/navigation/order/?new-order=%s' % ','.join(reversed([str(x.id) for x in entries]))
|
|
)
|
|
entries = PwaNavigationEntry.objects.all()
|
|
assert entries[0].label == 'Hello 2'
|
|
|
|
# remove first item
|
|
resp = app.get('/manage/pwa/')
|
|
resp = resp.click(href='delete', index=0)
|
|
resp = resp.form.submit().follow()
|
|
assert 'Hello 2' not in resp.text
|
|
assert 'Add a navigation entry' in resp.text
|
|
|
|
# rename item
|
|
resp = resp.click('Hello 1')
|
|
resp.form['label'] = 'Hello 12'
|
|
resp = resp.form.submit().follow()
|
|
assert PwaNavigationEntry.objects.all()[0].label == 'Hello 12'
|
|
|
|
# check error handling
|
|
resp = resp.click('Hello 12')
|
|
resp.form['label'] = ''
|
|
resp.form['url'] = ''
|
|
resp = resp.form.submit()
|
|
assert 'A label is required' in resp.text
|
|
assert 'An URL is required' in resp.text
|
|
|
|
resp.form['url'] = 'foobar'
|
|
resp.form['link_page'] = page.id
|
|
resp = resp.form.submit()
|
|
assert 'An URL cannot be specified' in resp.text
|
|
|
|
# add an icon
|
|
resp = app.get('/manage/pwa/')
|
|
resp.form['application_icon'] = Upload(
|
|
'test.png',
|
|
base64.decodebytes(
|
|
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
|
),
|
|
'image/png',
|
|
)
|
|
resp = resp.form.submit().follow()
|
|
assert PwaSettings.singleton().application_icon.name == 'pwa/test.png'
|
|
|
|
# make sure another application name was not saved
|
|
assert not PwaSettings.singleton().application_name
|
|
resp = app.get('/manage/pwa/')
|
|
assert resp.form['application_name'].value == 'Compte Citoyen'
|
|
resp.form['application_name'] = 'Test'
|
|
resp = resp.form.submit().follow()
|
|
assert PwaSettings.singleton().application_name == 'Test'
|
|
|
|
# make sure using the default application name switches back to
|
|
# global_title/compte citoyen.
|
|
resp.form['application_name'] = 'Compte Citoyen'
|
|
resp = resp.form.submit().follow()
|
|
assert not PwaSettings.singleton().application_name
|
|
|
|
# try an icon in an invalid format
|
|
resp.form['application_icon'] = Upload('test.txt', b'hello', 'text/plain')
|
|
resp = resp.form.submit()
|
|
assert 'The application icon must be in JPEG or PNG format' in resp
|
|
assert PwaSettings.singleton().application_icon.name == 'pwa/test.png'
|
|
|
|
|
|
def test_pwa_offline_page(app):
|
|
PwaSettings.objects.all().delete()
|
|
resp = app.get('/__pwa__/offline/')
|
|
assert 'You are currently offline.' in resp.text
|
|
assert 'Retry' in resp.text
|
|
pwa_settings = PwaSettings.singleton()
|
|
pwa_settings.offline_text = 'You are offline.'
|
|
pwa_settings.offline_retry_button = False
|
|
pwa_settings.save()
|
|
|
|
resp = app.get('/__pwa__/offline/')
|
|
assert 'You are offline.' in resp.text
|
|
assert 'Retry' not in resp.text
|
|
|
|
|
|
def test_pwa_navigation_templatetag(app):
|
|
page = Page(title='One', slug='one')
|
|
page.save()
|
|
entry1 = PwaNavigationEntry(
|
|
label='a', url='/', notification_count=True, use_user_name_as_label=True, order=0
|
|
)
|
|
entry2 = PwaNavigationEntry(link_page=page, order=1, icon=File(BytesIO(b'te\30st'), 'test.png'))
|
|
entry1.save()
|
|
entry2.save()
|
|
t = Template('{% load pwa %}{% pwa_navigation %}')
|
|
assert t.render(Context({})) == ''
|
|
|
|
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}):
|
|
request = RequestFactory().get('/')
|
|
nav = t.render(Context({'request': request}))
|
|
assert '<span>a</span>' in nav
|
|
assert '<span>One</span>' in nav
|
|
assert nav.count('background-image') == 1
|
|
assert nav.count('data-notification-count-url=') == 1
|
|
assert nav.count('data-pwa-user-name=""') == 1
|
|
|
|
nav = t.render(Context({'request': request, 'render_skeleton': True}))
|
|
assert 'data-pwa-user-name="{% block placeholder-user-name %}' in nav
|
|
|
|
|
|
def test_pwa_navigation_templatetag_internal_redirection(app):
|
|
page1 = Page(title='One', slug='one')
|
|
page2 = Page(title='Two', slug='two', redirect_url='http://www.example.org/test')
|
|
page3 = Page(title='Three', slug='three', redirect_url='../test')
|
|
page4 = Page(title='Four', slug='four', redirect_url='{{test_url}}plop')
|
|
page1.save()
|
|
page2.save()
|
|
page3.save()
|
|
page4.save()
|
|
entry1 = PwaNavigationEntry(link_page=page1, order=1)
|
|
entry2 = PwaNavigationEntry(link_page=page2, order=2)
|
|
entry3 = PwaNavigationEntry(link_page=page3, order=3)
|
|
entry4 = PwaNavigationEntry(link_page=page4, order=4)
|
|
entry1.save()
|
|
entry2.save()
|
|
entry3.save()
|
|
entry4.save()
|
|
t = Template('{% load pwa %}{% pwa_navigation %}')
|
|
|
|
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}):
|
|
request = RequestFactory().get('/')
|
|
with mock.patch('combo.apps.pwa.models.get_request', return_value=request):
|
|
nav = t.render(Context({'request': request}))
|
|
|
|
class MyHTMLParser(HTMLParser):
|
|
def handle_starttag(self, tag, attrs):
|
|
if tag == 'a':
|
|
results.append(attrs[0][1])
|
|
|
|
parser = MyHTMLParser()
|
|
results = []
|
|
parser.feed(nav)
|
|
assert results == [
|
|
'http://testserver/one/',
|
|
'http://www.example.org/test',
|
|
'http://testserver/three/',
|
|
'http://testserver/four/',
|
|
]
|
|
|
|
|
|
def test_pwa_application_icon(app, admin_user):
|
|
app = login(app)
|
|
with override_settings(TEMPLATE_VARS={'pwa_display': 'standalone'}):
|
|
PwaSettings.objects.all().delete()
|
|
resp = app.get('/manage/pwa/')
|
|
resp.form['application_icon'] = Upload(
|
|
'test.png',
|
|
base64.decodebytes(
|
|
b'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQAAAAA3bvkkAAAACklEQVQI12NoAAAAggCB3UNq9AAAAABJRU5ErkJggg=='
|
|
),
|
|
'image/png',
|
|
)
|
|
resp = resp.form.submit().follow()
|
|
|
|
resp = app.get('/manifest.json', status=200)
|
|
assert len(resp.json['icons']) == 5
|
|
|
|
# make sure largest icon is referenced in service worker
|
|
app.get('/service-worker.js', status=200)
|
|
assert resp.json['icons'][-1]['src'].split('/')[-1] in resp.text
|
|
|
|
|
|
def test_pwa_application_name(app, admin_user):
|
|
app = login(app)
|
|
assert app.get('/manifest.json', status=200).json['name'] == 'Compte Citoyen'
|
|
pwa_settings = PwaSettings.singleton()
|
|
pwa_settings.application_name = 'My wonderful app'
|
|
pwa_settings.save()
|
|
assert app.get('/manifest.json', status=200).json['name'] == 'My wonderful app'
|