diff --git a/changedetectionio/templates/watch-overview.html b/changedetectionio/templates/watch-overview.html
index a9297f03..c7c4c45e 100644
--- a/changedetectionio/templates/watch-overview.html
+++ b/changedetectionio/templates/watch-overview.html
@@ -30,6 +30,9 @@
@@ -84,7 +87,7 @@
{{watch.title if watch.title is not none and watch.title|length > 0 else watch.url}}
-
+
{%if watch.fetch_backend == "html_webdriver" %} {% endif %}
diff --git a/changedetectionio/tests/proxy_list/__init__.py b/changedetectionio/tests/proxy_list/__init__.py
new file mode 100644
index 00000000..085b3d78
--- /dev/null
+++ b/changedetectionio/tests/proxy_list/__init__.py
@@ -0,0 +1,2 @@
+"""Tests for the app."""
+
diff --git a/changedetectionio/tests/proxy_list/conftest.py b/changedetectionio/tests/proxy_list/conftest.py
new file mode 100644
index 00000000..95812e2e
--- /dev/null
+++ b/changedetectionio/tests/proxy_list/conftest.py
@@ -0,0 +1,14 @@
+#!/usr/bin/python3
+
+from .. import conftest
+
+#def pytest_addoption(parser):
+# parser.addoption("--url_suffix", action="store", default="identifier for request")
+
+
+#def pytest_generate_tests(metafunc):
+# # This is called for every test. Only get/set command line arguments
+# # if the argument is specified in the list of test "fixturenames".
+# option_value = metafunc.config.option.url_suffix
+# if 'url_suffix' in metafunc.fixturenames and option_value is not None:
+# metafunc.parametrize("url_suffix", [option_value])
\ No newline at end of file
diff --git a/changedetectionio/tests/proxy_list/proxies.json-example b/changedetectionio/tests/proxy_list/proxies.json-example
new file mode 100644
index 00000000..0ae2178c
--- /dev/null
+++ b/changedetectionio/tests/proxy_list/proxies.json-example
@@ -0,0 +1,10 @@
+{
+ "proxy-one": {
+ "label": "One",
+ "url": "http://127.0.0.1:3128"
+ },
+ "proxy-two": {
+ "label": "two",
+ "url": "http://127.0.0.1:3129"
+ }
+}
diff --git a/changedetectionio/tests/proxy_list/squid.conf b/changedetectionio/tests/proxy_list/squid.conf
new file mode 100644
index 00000000..615b154d
--- /dev/null
+++ b/changedetectionio/tests/proxy_list/squid.conf
@@ -0,0 +1,41 @@
+acl localnet src 0.0.0.1-0.255.255.255 # RFC 1122 "this" network (LAN)
+acl localnet src 10.0.0.0/8 # RFC 1918 local private network (LAN)
+acl localnet src 100.64.0.0/10 # RFC 6598 shared address space (CGN)
+acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines
+acl localnet src 172.16.0.0/12 # RFC 1918 local private network (LAN)
+acl localnet src 192.168.0.0/16 # RFC 1918 local private network (LAN)
+acl localnet src fc00::/7 # RFC 4193 local private network range
+acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines
+acl localnet src 159.65.224.174
+acl SSL_ports port 443
+acl Safe_ports port 80 # http
+acl Safe_ports port 21 # ftp
+acl Safe_ports port 443 # https
+acl Safe_ports port 70 # gopher
+acl Safe_ports port 210 # wais
+acl Safe_ports port 1025-65535 # unregistered ports
+acl Safe_ports port 280 # http-mgmt
+acl Safe_ports port 488 # gss-http
+acl Safe_ports port 591 # filemaker
+acl Safe_ports port 777 # multiling http
+acl CONNECT method CONNECT
+
+http_access deny !Safe_ports
+http_access deny CONNECT !SSL_ports
+http_access allow localhost manager
+http_access deny manager
+http_access allow localhost
+http_access allow localnet
+http_access deny all
+http_port 3128
+coredump_dir /var/spool/squid
+refresh_pattern ^ftp: 1440 20% 10080
+refresh_pattern ^gopher: 1440 0% 1440
+refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
+refresh_pattern \/(Packages|Sources)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
+refresh_pattern \/Release(|\.gpg)$ 0 0% 0 refresh-ims
+refresh_pattern \/InRelease$ 0 0% 0 refresh-ims
+refresh_pattern \/(Translation-.*)(|\.bz2|\.gz|\.xz)$ 0 0% 0 refresh-ims
+refresh_pattern . 0 20% 4320
+logfile_rotate 0
+
diff --git a/changedetectionio/tests/proxy_list/test_multiple_proxy.py b/changedetectionio/tests/proxy_list/test_multiple_proxy.py
new file mode 100644
index 00000000..fcd286eb
--- /dev/null
+++ b/changedetectionio/tests/proxy_list/test_multiple_proxy.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python3
+
+import time
+from flask import url_for
+from ..util import live_server_setup
+
+def test_preferred_proxy(client, live_server):
+ time.sleep(1)
+ live_server_setup(live_server)
+ time.sleep(1)
+ url = "http://chosen.changedetection.io"
+
+ res = client.post(
+ url_for("import_page"),
+ # Because a URL wont show in squid/proxy logs due it being SSLed
+ # Use plain HTTP or a specific domain-name here
+ data={"urls": url},
+ follow_redirects=True
+ )
+
+ assert b"1 Imported" in res.data
+
+ time.sleep(2)
+ res = client.post(
+ url_for("edit_page", uuid="first"),
+ data={
+ "css_filter": "",
+ "fetch_backend": "html_requests",
+ "headers": "",
+ "proxy": "proxy-two",
+ "tag": "",
+ "url": url,
+ },
+ follow_redirects=True
+ )
+ assert b"Updated watch." in res.data
+ time.sleep(2)
+ # Now the request should appear in the second-squid logs
diff --git a/changedetectionio/tests/proxy_list/test_proxy.py b/changedetectionio/tests/proxy_list/test_proxy.py
new file mode 100644
index 00000000..1f4c5ff4
--- /dev/null
+++ b/changedetectionio/tests/proxy_list/test_proxy.py
@@ -0,0 +1,19 @@
+#!/usr/bin/python3
+
+import time
+from flask import url_for
+from ..util import live_server_setup, wait_for_all_checks, extract_UUID_from_client
+
+# just make a request, we will grep in the docker logs to see it actually got called
+def test_check_basic_change_detection_functionality(client, live_server):
+ live_server_setup(live_server)
+ res = client.post(
+ url_for("import_page"),
+ # Because a URL wont show in squid/proxy logs due it being SSLed
+ # Use plain HTTP or a specific domain-name here
+ data={"urls": "http://one.changedetection.io"},
+ follow_redirects=True
+ )
+
+ assert b"1 Imported" in res.data
+ time.sleep(3)
diff --git a/changedetectionio/tests/test_api.py b/changedetectionio/tests/test_api.py
index dd66012e..504a9554 100644
--- a/changedetectionio/tests/test_api.py
+++ b/changedetectionio/tests/test_api.py
@@ -147,6 +147,16 @@ def test_api_simple(client, live_server):
# @todo how to handle None/default global values?
assert watch['history_n'] == 2, "Found replacement history section, which is in its own API"
+ # basic systeminfo check
+ res = client.get(
+ url_for("systeminfo"),
+ headers={'x-api-key': api_key},
+ )
+ info = json.loads(res.data)
+ assert info.get('watch_count') == 1
+ assert info.get('uptime') > 0.5
+
+
# Finally delete the watch
res = client.delete(
url_for("watch", uuid=watch_uuid),
diff --git a/changedetectionio/tests/test_backup.py b/changedetectionio/tests/test_backup.py
index 787d7fc0..89fd66a5 100644
--- a/changedetectionio/tests/test_backup.py
+++ b/changedetectionio/tests/test_backup.py
@@ -1,18 +1,31 @@
#!/usr/bin/python3
-import time
+from .util import set_original_response, set_modified_response, live_server_setup
from flask import url_for
from urllib.request import urlopen
-from . util import set_original_response, set_modified_response, live_server_setup
+from zipfile import ZipFile
+import re
+import time
def test_backup(client, live_server):
-
live_server_setup(live_server)
+ set_original_response()
+
# Give the endpoint time to spin up
time.sleep(1)
+ # Add our URL to the import page
+ res = client.post(
+ url_for("import_page"),
+ data={"urls": url_for('test_endpoint', _external=True)},
+ follow_redirects=True
+ )
+
+ assert b"1 Imported" in res.data
+ time.sleep(3)
+
res = client.get(
url_for("get_backup"),
follow_redirects=True
@@ -20,6 +33,19 @@ def test_backup(client, live_server):
# Should get the right zip content type
assert res.content_type == "application/zip"
+
# Should be PK/ZIP stream
assert res.data.count(b'PK') >= 2
+ # ZipFile from buffer seems non-obvious, just save it instead
+ with open("download.zip", 'wb') as f:
+ f.write(res.data)
+
+ zip = ZipFile('download.zip')
+ l = zip.namelist()
+ uuid4hex = re.compile('^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}.*txt', re.I)
+ newlist = list(filter(uuid4hex.match, l)) # Read Note below
+
+ # Should be two txt files in the archive (history and the snapshot)
+ assert len(newlist) == 2
+
diff --git a/changedetectionio/tests/test_jinja2.py b/changedetectionio/tests/test_jinja2.py
new file mode 100644
index 00000000..9c6baa9f
--- /dev/null
+++ b/changedetectionio/tests/test_jinja2.py
@@ -0,0 +1,33 @@
+#!/usr/bin/python3
+
+import time
+from flask import url_for
+from .util import live_server_setup
+
+
+# If there was only a change in the whitespacing, then we shouldnt have a change detected
+def test_jinja2_in_url_query(client, live_server):
+ live_server_setup(live_server)
+
+ # Give the endpoint time to spin up
+ time.sleep(1)
+
+ # Add our URL to the import page
+ test_url = url_for('test_return_query', _external=True)
+
+ # because url_for() will URL-encode the var, but we dont here
+ full_url = "{}?{}".format(test_url,
+ "date={% now 'Europe/Berlin', '%Y' %}.{% now 'Europe/Berlin', '%m' %}.{% now 'Europe/Berlin', '%d' %}", )
+ res = client.post(
+ url_for("form_quick_watch_add"),
+ data={"url": full_url, "tag": "test"},
+ follow_redirects=True
+ )
+ assert b"Watch added" in res.data
+ time.sleep(3)
+ # It should report nothing found (no new 'unviewed' class)
+ res = client.get(
+ url_for("preview_page", uuid="first"),
+ follow_redirects=True
+ )
+ assert b'date=2' in res.data
diff --git a/changedetectionio/tests/test_jsonpath_selector.py b/changedetectionio/tests/test_jsonpath_jq_selector.py
similarity index 83%
rename from changedetectionio/tests/test_jsonpath_selector.py
rename to changedetectionio/tests/test_jsonpath_jq_selector.py
index 729a201d..f6da84db 100644
--- a/changedetectionio/tests/test_jsonpath_selector.py
+++ b/changedetectionio/tests/test_jsonpath_jq_selector.py
@@ -2,10 +2,15 @@
# coding=utf-8
import time
-from flask import url_for
+from flask import url_for, escape
from . util import live_server_setup
import pytest
+jq_support = True
+try:
+ import jq
+except ModuleNotFoundError:
+ jq_support = False
def test_setup(live_server):
live_server_setup(live_server)
@@ -36,16 +41,28 @@ and it can also be repeated
from .. import html_tools
# See that we can find the second |