mirror of
https://github.com/YunoHost-Apps/django-fmd_ynh.git
synced 2024-09-03 18:26:27 +02:00
Fix and expand tests
This commit is contained in:
parent
efe27ae398
commit
178f6c91c1
5 changed files with 324 additions and 17 deletions
33
poetry.lock
generated
33
poetry.lock
generated
|
@ -53,6 +53,21 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
||||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
||||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
|
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "beautifulsoup4"
|
||||||
|
version = "4.11.1"
|
||||||
|
description = "Screen-scraping library"
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.0"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
soupsieve = ">1.2"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
html5lib = ["html5lib"]
|
||||||
|
lxml = ["lxml"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "22.6.0"
|
version = "22.6.0"
|
||||||
|
@ -746,6 +761,14 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soupsieve"
|
||||||
|
version = "2.3.2.post1"
|
||||||
|
description = "A modern CSS selector implementation for Beautiful Soup."
|
||||||
|
category = "dev"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparse"
|
name = "sqlparse"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -880,7 +903,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = ">=3.7,<4.0.0"
|
python-versions = ">=3.7,<4.0.0"
|
||||||
content-hash = "efbe068c32696bda78b96c5bc9d05217a92a8f19ef832d444cdd6e6b99a5223c"
|
content-hash = "0262d33ea15ad4ec9e8234cd55634c1c34df4ce3f878c268fb1b3c8da5b94f7a"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
asgiref = [
|
asgiref = [
|
||||||
|
@ -902,6 +925,10 @@ attrs = [
|
||||||
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
|
||||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||||
]
|
]
|
||||||
|
beautifulsoup4 = [
|
||||||
|
{file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"},
|
||||||
|
{file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
|
||||||
|
]
|
||||||
black = [
|
black = [
|
||||||
{file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
|
{file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"},
|
||||||
{file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
|
{file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"},
|
||||||
|
@ -1193,6 +1220,10 @@ six = [
|
||||||
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
|
||||||
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
|
||||||
]
|
]
|
||||||
|
soupsieve = [
|
||||||
|
{file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
|
||||||
|
{file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
|
||||||
|
]
|
||||||
sqlparse = [
|
sqlparse = [
|
||||||
{file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"},
|
{file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"},
|
||||||
{file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"},
|
{file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"},
|
||||||
|
|
|
@ -30,6 +30,7 @@ flake8 = "*"
|
||||||
flynt = "*"
|
flynt = "*"
|
||||||
darker = "*" # https://github.com/akaihola/darker
|
darker = "*" # https://github.com/akaihola/darker
|
||||||
pyupgrade = "*"
|
pyupgrade = "*"
|
||||||
|
beautifulsoup4 = "*"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin
|
from bx_django_utils.test_utils.html_assertion import (
|
||||||
|
HtmlAssertionMixin,
|
||||||
|
assert_html_response_snapshot,
|
||||||
|
)
|
||||||
from django.conf import LazySettings, settings
|
from django.conf import LazySettings, settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.http import FileResponse, HttpResponse
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
from django.test.testcases import TestCase
|
from django.test.testcases import TestCase
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
|
@ -10,7 +14,7 @@ from django.urls.base import reverse
|
||||||
from django_yunohost_integration.test_utils import generate_basic_auth
|
from django_yunohost_integration.test_utils import generate_basic_auth
|
||||||
from django_yunohost_integration.views import request_media_debug_view
|
from django_yunohost_integration.views import request_media_debug_view
|
||||||
|
|
||||||
import findmydevice
|
from findmydevice import __version__
|
||||||
|
|
||||||
|
|
||||||
@override_settings(DEBUG=False)
|
@override_settings(DEBUG=False)
|
||||||
|
@ -57,23 +61,43 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.client.get('/app_path/', secure=True)
|
response = self.client.get('/app_path/', secure=True)
|
||||||
self.assertRedirects(
|
self.assertTemplateUsed(response, 'fmd/login_info.html')
|
||||||
response, expected_url='/app_path/group_management/', fetch_redirect_response=False
|
self.assert_html_parts(
|
||||||
)
|
|
||||||
|
|
||||||
response = self.client.get('/app_path/group_management/', secure=True)
|
|
||||||
self.assertRedirects(
|
|
||||||
response,
|
response,
|
||||||
expected_url='/app_path/admin/login/?next=/app_path/group_management/',
|
parts=(
|
||||||
fetch_redirect_response=False,
|
'<title>Log in | Find My Device</title>',
|
||||||
|
'<p class="errornote">To find your device, you must be logged in.</p>',
|
||||||
|
'<a href="/yunohost/sso/">Log in</a>',
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
assert_html_response_snapshot(response, query_selector=None, validate=False)
|
||||||
|
|
||||||
response = self.client.get('/app_path/admin/', secure=True)
|
def test_web_page_as_sso_user(self):
|
||||||
self.assertRedirects(
|
assert User.objects.count() == 0
|
||||||
response,
|
self.client.cookies['SSOwAuthUser'] = 'test'
|
||||||
expected_url='/app_path/admin/login/?next=/app_path/admin/',
|
response = self.client.get(
|
||||||
fetch_redirect_response=False,
|
path='/app_path/',
|
||||||
|
HTTP_REMOTE_USER='test',
|
||||||
|
HTTP_AUTH_USER='test',
|
||||||
|
HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
|
||||||
|
secure=True,
|
||||||
)
|
)
|
||||||
|
assert isinstance(response, FileResponse)
|
||||||
|
|
||||||
|
assert User.objects.count() == 1
|
||||||
|
user = User.objects.first()
|
||||||
|
assert user.username == 'test'
|
||||||
|
|
||||||
|
response2 = HttpResponse(response.getvalue())
|
||||||
|
self.assert_html_parts(
|
||||||
|
response2,
|
||||||
|
parts=(
|
||||||
|
'<title>FMD</title>',
|
||||||
|
'<h2>Find My Device</h2>',
|
||||||
|
'<script src="./logic.js"></script>',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert_html_response_snapshot(response2, query_selector=None, validate=False)
|
||||||
|
|
||||||
@override_settings(SECURE_SSL_REDIRECT=False)
|
@override_settings(SECURE_SSL_REDIRECT=False)
|
||||||
def test_create_unknown_user(self):
|
def test_create_unknown_user(self):
|
||||||
|
@ -99,8 +123,9 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
|
||||||
self.assert_html_parts(
|
self.assert_html_parts(
|
||||||
response,
|
response,
|
||||||
parts=(
|
parts=(
|
||||||
'<title>Site administration | Find My Device</title>',
|
'<title>Site administration | Find My Device admin</title>',
|
||||||
'<strong>test</strong>',
|
'<strong>test</strong>',
|
||||||
|
f'<a href="/app_path/admin/">Django Find My Device v{__version__}</a>',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
55
tests/test_django_project_auth_1.snapshot.html
Normal file
55
tests/test_django_project_auth_1.snapshot.html
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html dir="ltr" lang="en">
|
||||||
|
<head>
|
||||||
|
<title>
|
||||||
|
Log in | Find My Device
|
||||||
|
</title>
|
||||||
|
<link href="/app_path/static/admin/css/base.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<meta content="notranslate" name="google"/>
|
||||||
|
<meta content="noindex,nofollow" name="robots">
|
||||||
|
<meta content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0" name="viewport"/>
|
||||||
|
<link href="/app_path/static/admin/css/responsive.css" rel="stylesheet" type="text/css"/>
|
||||||
|
<meta content="NONE,NOARCHIVE" name="robots"/>
|
||||||
|
</meta>
|
||||||
|
</head>
|
||||||
|
<body class="" data-admin-utc-offset="7200">
|
||||||
|
<!-- Container -->
|
||||||
|
<div id="container">
|
||||||
|
<!-- Header -->
|
||||||
|
<div id="header">
|
||||||
|
<div id="branding">
|
||||||
|
<h1>
|
||||||
|
Find My Device
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END Header -->
|
||||||
|
<div class="main shifted" id="main">
|
||||||
|
<div class="content">
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="colM" id="content">
|
||||||
|
<h1>
|
||||||
|
Log in
|
||||||
|
</h1>
|
||||||
|
<p class="errornote">
|
||||||
|
To find your device, you must be logged in.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="/yunohost/sso/">
|
||||||
|
Log in
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<br class="clear"/>
|
||||||
|
</div>
|
||||||
|
<!-- END Content -->
|
||||||
|
<div class="submit-row" id="footer">
|
||||||
|
<a href="https://gitlab.com/jedie/django-find-my-device">
|
||||||
|
gitlab.com/jedie/django-find-my-device
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- END Container -->
|
||||||
|
</body>
|
||||||
|
</html>
|
195
tests/test_django_project_web_page_as_sso_user_1.snapshot.html
Normal file
195
tests/test_django_project_web_page_as_sso_user_1.snapshot.html
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>
|
||||||
|
FMD
|
||||||
|
</title>
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||||
|
<meta content="no-cache, must-revalidate, post-check=0, pre-check=0" http-equiv="cache-control">
|
||||||
|
<meta content="max-age=0" http-equiv="cache-control"/>
|
||||||
|
<meta content="0" http-equiv="expires"/>
|
||||||
|
<link href="./style.css?t=123456" rel="stylesheet"/>
|
||||||
|
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js">
|
||||||
|
</script>
|
||||||
|
<link href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" rel="stylesheet">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.js" type="text/javascript">
|
||||||
|
</script>
|
||||||
|
<script async="" src="https://cdnjs.cloudflare.com/ajax/libs/jsencrypt/2.3.1/jsencrypt.min.js">
|
||||||
|
</script>
|
||||||
|
<link href="https://unpkg.com/toastedjs/dist/toasted.min.css" rel="stylesheet"/>
|
||||||
|
<script src="https://unpkg.com/toastedjs/dist/toasted.min.js">
|
||||||
|
</script>
|
||||||
|
<script src="./logic.js">
|
||||||
|
</script>
|
||||||
|
<body onload="init();">
|
||||||
|
<div id="head">
|
||||||
|
<div class="center">
|
||||||
|
<h2>
|
||||||
|
Find My Device
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div id="login">
|
||||||
|
<div class="center">
|
||||||
|
<p>
|
||||||
|
This server tracks all your registered devices.
|
||||||
|
<br/>
|
||||||
|
Please enter the device-id to receive the latest
|
||||||
|
location:
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div class="center">
|
||||||
|
<input id="fmdid" name="fmdid" onkeypress="clickPress(event)" type="text"/>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<button id="locateButton" onclick="prepareForLocate();" type="button">
|
||||||
|
LOCATE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="dataView" onkeypress="switchWithKeys(event)">
|
||||||
|
<div id="map">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div>
|
||||||
|
<div class="deviceInfo" id="desktop">
|
||||||
|
<div class="row">
|
||||||
|
<div class="column-left">
|
||||||
|
<div class="center">
|
||||||
|
<img height="96" src="assets/Smartphone.png"/>
|
||||||
|
</div>
|
||||||
|
<div class="center" style="margin-top:1vw">
|
||||||
|
<p id="batView">
|
||||||
|
Data
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column-middle">
|
||||||
|
<h3 id="idView" style="display:inline;">
|
||||||
|
ID
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
-
|
||||||
|
</p>
|
||||||
|
<p id="providerView">
|
||||||
|
Data
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button class="imageButton" onclick="locateOlder()" type="button">
|
||||||
|
<img height="24" src="assets/Left.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="locateNewer()" type="button">
|
||||||
|
<img height="24" src="assets/Right.png"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p id="datetime">
|
||||||
|
data from
|
||||||
|
</p>
|
||||||
|
<p id="dateView">
|
||||||
|
Date
|
||||||
|
</p>
|
||||||
|
<p id="timeView">
|
||||||
|
Time
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('locate')" title="Locate" type="button">
|
||||||
|
<img height="36" src="assets/Locate.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('ring')" title="Ring" type="button">
|
||||||
|
<img height="36" src="assets/Ring.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('lock')" title="Lock" type="button">
|
||||||
|
<img height="36" src="assets/Locked.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('delete')" title="Delete" type="button">
|
||||||
|
<img height="36" src="assets/Delete.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="showPicture()" title="Picture" type="button">
|
||||||
|
<img height="36" src="assets/Picture.png"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="deviceInfo" id="mobile">
|
||||||
|
<div class="row">
|
||||||
|
<div class="column-left">
|
||||||
|
<div class="center">
|
||||||
|
<img height="48" src="assets/Smartphone.png"/>
|
||||||
|
</div>
|
||||||
|
<div class="center" style="margin-top:1vw">
|
||||||
|
<p id="batView">
|
||||||
|
Data
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column-middle">
|
||||||
|
<h3 id="idView" style="display:inline;">
|
||||||
|
ID
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
-
|
||||||
|
</p>
|
||||||
|
<p id="providerView">
|
||||||
|
Data
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<button class="imageButton" onclick="locateOlder()" type="button">
|
||||||
|
<img height="24" src="assets/Left.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="locateNewer()" type="button">
|
||||||
|
<img height="24" src="assets/Right.png"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p id="datetime">
|
||||||
|
data from
|
||||||
|
</p>
|
||||||
|
<p id="dateView">
|
||||||
|
Date
|
||||||
|
</p>
|
||||||
|
<p id="timeView">
|
||||||
|
Time
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('locate')" title="Locate" type="button">
|
||||||
|
<img height="24" src="assets/Locate.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('ring')" title="Ring" type="button">
|
||||||
|
<img height="24" src="assets/Ring.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('lock')" title="Lock" type="button">
|
||||||
|
<img height="24" src="assets/Locked.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="sendToPhone('delete')" title="Delete" type="button">
|
||||||
|
<img height="24" src="assets/Delete.png"/>
|
||||||
|
</button>
|
||||||
|
<button class="imageButton" onclick="showPicture()" title="Picture" type="button">
|
||||||
|
<img height="24" src="assets/Picture.png"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="center">
|
||||||
|
<p>
|
||||||
|
@Nulide FMDServer
|
||||||
|
</p>
|
||||||
|
<p id="version">
|
||||||
|
v
|
||||||
|
</p>
|
||||||
|
<a href="ds.html">
|
||||||
|
Privacy Notice
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</link>
|
||||||
|
</meta>
|
||||||
|
</head>
|
||||||
|
</html>
|
Loading…
Reference in a new issue