diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 413ce4b1..7920383a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,11 +4,13 @@ updates: directory: / schedule: interval: "weekly" - "caronc/apprise": - versioning-strategy: "increase" - schedule: - interval: "daily" groups: all: patterns: - "*" + - package-ecosystem: pip + directory: / + schedule: + interval: "daily" + allow: + - dependency-name: "apprise" diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index a93dedbf..6c591cff 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -95,7 +95,7 @@ jobs: push: true tags: | ${{ secrets.DOCKER_HUB_USERNAME }}/changedetection.io:dev,ghcr.io/${{ github.repository }}:dev - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/arm64/v8 + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8 cache-from: type=gha cache-to: type=gha,mode=max @@ -133,7 +133,7 @@ jobs: file: ./Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/arm64/v8 + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8 cache-from: type=gha cache-to: type=gha,mode=max # Looks like this was disabled diff --git a/.github/workflows/test-container-build.yml b/.github/workflows/test-container-build.yml index 4c0783eb..48838e82 100644 --- a/.github/workflows/test-container-build.yml +++ b/.github/workflows/test-container-build.yml @@ -38,8 +38,6 @@ jobs: dockerfile: ./Dockerfile - platform: linux/arm/v8 dockerfile: ./Dockerfile - - platform: linux/arm64/v8 - dockerfile: ./Dockerfile # Alpine Dockerfile platforms (musl via alpine check) - platform: linux/amd64 dockerfile: ./.github/test/Dockerfile-alpine @@ -76,5 +74,5 @@ jobs: file: ${{ matrix.dockerfile }} platforms: ${{ matrix.platform }} cache-from: type=gha - cache-to: type=gha,mode=max + cache-to: type=gha,mode=min diff --git a/changedetectionio/blueprint/settings/templates/settings.html b/changedetectionio/blueprint/settings/templates/settings.html index a6114830..986908ab 100644 --- a/changedetectionio/blueprint/settings/templates/settings.html +++ b/changedetectionio/blueprint/settings/templates/settings.html @@ -192,6 +192,12 @@ nav +
+ {{ render_checkbox_field(form.application.form.strip_ignored_lines) }} + Remove any text that appears in the "Ignore text" from the output (otherwise its just ignored for change-detection)
+ Note: Changing this will change the status of your existing watches, possibly trigger alerts etc. +
+
diff --git a/changedetectionio/blueprint/watchlist/__init__.py b/changedetectionio/blueprint/watchlist/__init__.py index d7cbe8e9..009670d8 100644 --- a/changedetectionio/blueprint/watchlist/__init__.py +++ b/changedetectionio/blueprint/watchlist/__init__.py @@ -87,7 +87,6 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe form=form, guid=datastore.data['app_guid'], has_proxies=datastore.proxy_list, - has_unviewed=datastore.has_unviewed, hosted_sticky=os.getenv("SALTED_PASS", False) == False, now_time_server=round(time.time()), pagination=pagination, @@ -97,6 +96,7 @@ def construct_blueprint(datastore: ChangeDetectionStore, update_q, queuedWatchMe sort_order=request.args.get('order') if request.args.get('order') else request.cookies.get('order'), system_default_fetcher=datastore.data['settings']['application'].get('fetch_backend'), tags=sorted_tags, + unread_changes_count=datastore.unread_changes_count, watches=sorted_watches ) diff --git a/changedetectionio/blueprint/watchlist/templates/watch-overview.html b/changedetectionio/blueprint/watchlist/templates/watch-overview.html index df5bd5fe..1cb22c67 100644 --- a/changedetectionio/blueprint/watchlist/templates/watch-overview.html +++ b/changedetectionio/blueprint/watchlist/templates/watch-overview.html @@ -82,8 +82,11 @@ document.addEventListener('DOMContentLoaded', function() { {%- set cols_required = cols_required + 1 -%} {%- endif -%} {%- set ui_settings = datastore.data['settings']['application']['ui'] -%} - -
+ {%- set wrapper_classes = [ + 'has-unread-changes' if unread_changes_count else '', + 'has-error' if errored_count else '', + ] -%} +
{%- set table_classes = [ 'favicon-enabled' if 'favicons_enabled' not in ui_settings or ui_settings['favicons_enabled'] else 'favicon-not-enabled', ] -%} @@ -241,10 +244,10 @@ document.addEventListener('DOMContentLoaded', function() { - +

+
+ {{ render_ternary_field(form.strip_ignored_lines) }} +
diff --git a/changedetectionio/tests/test_add_replace_remove_filter.py b/changedetectionio/tests/test_add_replace_remove_filter.py index c3479e97..c1a71c79 100644 --- a/changedetectionio/tests/test_add_replace_remove_filter.py +++ b/changedetectionio/tests/test_add_replace_remove_filter.py @@ -75,7 +75,7 @@ def test_check_removed_line_contains_trigger(client, live_server, measure_memory wait_for_all_checks(client) time.sleep(0.5) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # The trigger line is REMOVED, this should trigger set_original(excluding='The golden line') @@ -84,7 +84,7 @@ def test_check_removed_line_contains_trigger(client, live_server, measure_memory client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data time.sleep(1) @@ -98,14 +98,14 @@ def test_check_removed_line_contains_trigger(client, live_server, measure_memory wait_for_all_checks(client) time.sleep(1) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Remove it again, and we should get a trigger set_original(excluding='The golden line') client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data @@ -169,7 +169,7 @@ def test_check_add_line_contains_trigger(client, live_server, measure_memory_usa wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # The trigger line is ADDED, this should trigger set_original(add_line='

Oh yes please

') @@ -177,7 +177,7 @@ def test_check_add_line_contains_trigger(client, live_server, measure_memory_usa wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Takes a moment for apprise to fire wait_for_notification_endpoint_output() diff --git a/changedetectionio/tests/test_backend.py b/changedetectionio/tests/test_backend.py index c250e32d..2636466e 100644 --- a/changedetectionio/tests/test_backend.py +++ b/changedetectionio/tests/test_backend.py @@ -38,9 +38,9 @@ def test_check_basic_change_detection_functionality(client, live_server, measure # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'test-endpoint' in res.data # Default no password set, this stuff should be always available. @@ -74,9 +74,9 @@ def test_check_basic_change_detection_functionality(client, live_server, measure res = client.get(url_for("ui.ui_edit.watch_get_latest_html", uuid=uuid)) assert b'which has this one new line' in res.data - # Now something should be ready, indicated by having a 'unviewed' class + # Now something should be ready, indicated by having a 'has-unread-changes' class res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # #75, and it should be in the RSS feed rss_token = extract_rss_token_from_UI(client) @@ -90,7 +90,7 @@ def test_check_basic_change_detection_functionality(client, live_server, measure assert expected_url.encode('utf-8') in res.data # - # Following the 'diff' link, it should no longer display as 'unviewed' even after we recheck it a few times + # Following the 'diff' link, it should no longer display as 'has-unread-changes' even after we recheck it a few times res = client.get(url_for("ui.ui_views.diff_history_page", uuid=uuid)) assert b'selected=""' in res.data, "Confirm diff history page loaded" @@ -111,12 +111,12 @@ def test_check_basic_change_detection_functionality(client, live_server, measure # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data - assert b'class="has-unviewed' not in res.data + assert b'has-unread-changes' not in res.data + assert b'class="has-unread-changes' not in res.data assert b'head title' in res.data # Should be ON by default assert b'test-endpoint' in res.data @@ -140,8 +140,8 @@ def test_check_basic_change_detection_functionality(client, live_server, measure wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data - assert b'class="has-unviewed' in res.data + assert b'has-unread-changes' in res.data + assert b'class="has-unread-changes' in res.data assert b'head title' not in res.data # should now be off @@ -151,8 +151,8 @@ def test_check_basic_change_detection_functionality(client, live_server, measure # hit the mark all viewed link res = client.get(url_for("ui.mark_all_viewed"), follow_redirects=True) - assert b'class="has-unviewed' not in res.data - assert b'unviewed' not in res.data + assert b'class="has-unread-changes' not in res.data + assert b'has-unread-changes' not in res.data # #2458 "clear history" should make the Watch object update its status correctly when the first snapshot lands again client.get(url_for("ui.clear_watch_history", uuid=uuid)) @@ -165,3 +165,53 @@ def test_check_basic_change_detection_functionality(client, live_server, measure # Cleanup everything res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data + +def test_non_text_mime_or_downloads(client, live_server, measure_memory_usage): + with open("test-datastore/endpoint-content.txt", "w") as f: + f.write("""some random text that should be split by line +and not parsed with html_to_text +this way we know that it correctly parsed as plain text +\r\n +ok\r\n +got it\r\n +""") + + test_url = url_for('test_endpoint', content_type="application/octet-stream", _external=True) + + # Add our URL to the import page + res = client.post( + url_for("imports.import_page"), + data={"urls": test_url}, + follow_redirects=True + ) + + assert b"1 Imported" in res.data + + wait_for_all_checks(client) + + ### check the front end + res = client.get( + url_for("ui.ui_views.preview_page", uuid="first"), + follow_redirects=True + ) + assert b"some random text that should be split by line\n" in res.data + #### + + # Check the snapshot by API that it has linefeeds too + watch_uuid = next(iter(live_server.app.config['DATASTORE'].data['watching'])) + api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token') + res = client.get( + url_for("watchhistory", uuid=watch_uuid), + headers={'x-api-key': api_key}, + ) + + # Fetch a snapshot by timestamp, check the right one was found + res = client.get( + url_for("watchsinglehistory", uuid=watch_uuid, timestamp=list(res.json.keys())[-1]), + headers={'x-api-key': api_key}, + ) + assert b"some random text that should be split by line\n" in res.data + + + res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) + diff --git a/changedetectionio/tests/test_basic_socketio.py b/changedetectionio/tests/test_basic_socketio.py index 1c48e758..120907dd 100644 --- a/changedetectionio/tests/test_basic_socketio.py +++ b/changedetectionio/tests/test_basic_socketio.py @@ -58,6 +58,7 @@ def run_socketio_watch_update_test(client, live_server, password_mode=""): has_watch_update = False has_unviewed_update = False + got_general_stats_update = False for i in range(10): # Get received events @@ -65,15 +66,11 @@ def run_socketio_watch_update_test(client, live_server, password_mode=""): if received: logger.info(f"Received {len(received)} events after {i+1} seconds") - - # Check for watch_update events with unviewed=True for event in received: if event['name'] == 'watch_update': has_watch_update = True - if event['args'][0]['watch'].get('unviewed', False): - has_unviewed_update = True - logger.info("Found unviewed update event!") - break + if event['name'] == 'general_stats_update': + got_general_stats_update = True if has_unviewed_update: break @@ -92,7 +89,7 @@ def run_socketio_watch_update_test(client, live_server, password_mode=""): assert has_watch_update, "No watch_update events received" # Verify we received an unviewed event - assert has_unviewed_update, "No watch_update event with unviewed=True received" + assert got_general_stats_update, "Got general stats update event" # Alternatively, check directly if the watch in the datastore is marked as unviewed from changedetectionio.flask_app import app diff --git a/changedetectionio/tests/test_block_while_text_present.py b/changedetectionio/tests/test_block_while_text_present.py index 6e97b826..bf26a3de 100644 --- a/changedetectionio/tests/test_block_while_text_present.py +++ b/changedetectionio/tests/test_block_while_text_present.py @@ -107,9 +107,9 @@ def test_check_block_changedetection_text_NOT_present(client, live_server, measu # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data # The page changed, BUT the text is still there, just the rest of it changes, we should not see a change @@ -120,9 +120,9 @@ def test_check_block_changedetection_text_NOT_present(client, live_server, measu # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data # 2548 @@ -131,7 +131,7 @@ def test_check_block_changedetection_text_NOT_present(client, live_server, measu client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Now we set a change where the text is gone AND its different content, it should now trigger @@ -139,7 +139,7 @@ def test_check_block_changedetection_text_NOT_present(client, live_server, measu client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data diff --git a/changedetectionio/tests/test_conditions.py b/changedetectionio/tests/test_conditions.py index e06f8044..2685ae75 100644 --- a/changedetectionio/tests/test_conditions.py +++ b/changedetectionio/tests/test_conditions.py @@ -125,7 +125,7 @@ def test_conditions_with_text_and_number(client, live_server): time.sleep(2) # 75 is > 20 and < 100 and contains "5" res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Case 2: Change with one condition violated @@ -141,7 +141,7 @@ def test_conditions_with_text_and_number(client, live_server): # Should NOT be marked as having changes since not all conditions are met res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data @@ -299,7 +299,7 @@ def test_lev_conditions_plugin(client, live_server, measure_memory_usage): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Check the content saved initially, even tho a condition was set - this is the first snapshot so shouldnt be affected by conditions res = client.get( @@ -326,7 +326,7 @@ def test_lev_conditions_plugin(client, live_server, measure_memory_usage): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data #because this will be like 0.90 not 0.8 threshold + assert b'has-unread-changes' not in res.data #because this will be like 0.90 not 0.8 threshold ############### Now change it a MORE THAN 50% test_return_data = """ @@ -345,7 +345,7 @@ def test_lev_conditions_plugin(client, live_server, measure_memory_usage): assert b'Queued 1 watch for rechecking.' in res.data wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # cleanup for the next client.get( url_for("ui.form_delete", uuid="all"), diff --git a/changedetectionio/tests/test_css_selector.py b/changedetectionio/tests/test_css_selector.py index 1cbd6c43..b9c6a839 100644 --- a/changedetectionio/tests/test_css_selector.py +++ b/changedetectionio/tests/test_css_selector.py @@ -116,10 +116,10 @@ def test_check_markup_include_filters_restriction(client, live_server, measure_m # Give the thread time to pick it up time.sleep(sleep_time_for_fetch_thread) - # It should have 'unviewed' still + # It should have 'has-unread-changes' still # Because it should be looking at only that 'sametext' id res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Tests the whole stack works with the CSS Filter diff --git a/changedetectionio/tests/test_element_removal.py b/changedetectionio/tests/test_element_removal.py index 9ce1d0fa..3b6ad3d1 100644 --- a/changedetectionio/tests/test_element_removal.py +++ b/changedetectionio/tests/test_element_removal.py @@ -190,7 +190,7 @@ def test_element_removal_full(client, live_server, measure_memory_usage): wait_for_all_checks(client) - # so that we set the state to 'unviewed' after all the edits + # so that we set the state to 'has-unread-changes' after all the edits client.get(url_for("ui.ui_views.diff_history_page", uuid="first")) # Make a change to header/footer/nav diff --git a/changedetectionio/tests/test_errorhandling.py b/changedetectionio/tests/test_errorhandling.py index f79c4dda..83b192b4 100644 --- a/changedetectionio/tests/test_errorhandling.py +++ b/changedetectionio/tests/test_errorhandling.py @@ -31,7 +31,7 @@ def _runner_test_http_errors(client, live_server, http_code, expected_text): res = client.get(url_for("watchlist.index")) # no change - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert bytes(expected_text.encode('utf-8')) in res.data diff --git a/changedetectionio/tests/test_extract_regex.py b/changedetectionio/tests/test_extract_regex.py index 82ef12c4..8276296e 100644 --- a/changedetectionio/tests/test_extract_regex.py +++ b/changedetectionio/tests/test_extract_regex.py @@ -174,10 +174,10 @@ def test_check_filter_and_regex_extract(client, live_server, measure_memory_usag # Give the thread time to pick it up wait_for_all_checks(client) - # It should have 'unviewed' still + # It should have 'has-unread-changes' still # Because it should be looking at only that 'sametext' id res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Check HTML conversion detected and workd res = client.get( diff --git a/changedetectionio/tests/test_ignore.py b/changedetectionio/tests/test_ignore.py index 985e58b8..7f6aa62a 100644 --- a/changedetectionio/tests/test_ignore.py +++ b/changedetectionio/tests/test_ignore.py @@ -58,3 +58,39 @@ def test_ignore(client, live_server, measure_memory_usage): # Should be in base.html assert b'csrftoken' in res.data + +def test_strip_ignore_lines(client, live_server, measure_memory_usage): + # live_server_setup(live_server) # Setup on conftest per function + set_original_ignore_response() + + + # Goto the settings page, add our ignore text + res = client.post( + url_for("settings.settings_page"), + data={ + "requests-time_between_check-minutes": 180, + "application-ignore_whitespace": "y", + "application-strip_ignored_lines": "y", + "application-global_ignore_text": "Which is across multiple", + 'application-fetch_backend': "html_requests" + }, + follow_redirects=True + ) + assert b"Settings updated." in res.data + + test_url = url_for('test_endpoint', _external=True) + res = client.post( + url_for("imports.import_page"), + data={"urls": test_url}, + follow_redirects=True + ) + assert b"1 Imported" in res.data + + # Give the thread time to pick it up + wait_for_all_checks(client) + uuid = next(iter(live_server.app.config['DATASTORE'].data['watching'])) + + # It should not be in the preview anymore + res = client.get(url_for("ui.ui_views.preview_page", uuid=uuid)) + assert b'
' not in res.data + assert b'Which is across multiple' not in res.data diff --git a/changedetectionio/tests/test_ignore_text.py b/changedetectionio/tests/test_ignore_text.py index 1755e295..0b76bf87 100644 --- a/changedetectionio/tests/test_ignore_text.py +++ b/changedetectionio/tests/test_ignore_text.py @@ -128,9 +128,9 @@ def test_check_ignore_text_functionality(client, live_server, measure_memory_usa # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data # Make a change @@ -141,9 +141,9 @@ def test_check_ignore_text_functionality(client, live_server, measure_memory_usa # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data @@ -154,7 +154,7 @@ def test_check_ignore_text_functionality(client, live_server, measure_memory_usa wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.ui_views.preview_page", uuid="first")) @@ -222,9 +222,9 @@ def _run_test_global_ignore(client, as_source=False, extra_ignore=""): # Trigger a check client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class), adding random ignore text should not cause a change + # It should report nothing found (no new 'has-unread-changes' class), adding random ignore text should not cause a change res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data ##### @@ -238,10 +238,10 @@ def _run_test_global_ignore(client, as_source=False, extra_ignore=""): # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data # Just to be sure.. set a regular modified change that will trigger it @@ -249,7 +249,7 @@ def _run_test_global_ignore(client, as_source=False, extra_ignore=""): client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data diff --git a/changedetectionio/tests/test_ignorehyperlinks.py b/changedetectionio/tests/test_ignorehyperlinks.py index 5df8f9ae..01ce2a3b 100644 --- a/changedetectionio/tests/test_ignorehyperlinks.py +++ b/changedetectionio/tests/test_ignorehyperlinks.py @@ -111,7 +111,7 @@ def test_render_anchor_tag_content_true(client, live_server, measure_memory_usag assert '(/modified_link)' in res.data.decode() # since the link has changed, and we chose to render anchor tag content, - # we should detect a change (new 'unviewed' class) + # we should detect a change (new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) assert b"unviewed" in res.data assert b"/test-endpoint" in res.data diff --git a/changedetectionio/tests/test_ignorestatuscode.py b/changedetectionio/tests/test_ignorestatuscode.py index 45bf9616..1f4f2649 100644 --- a/changedetectionio/tests/test_ignorestatuscode.py +++ b/changedetectionio/tests/test_ignorestatuscode.py @@ -77,9 +77,9 @@ def test_normal_page_check_works_with_ignore_status_code(client, live_server, me # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data assert b'/test-endpoint' in res.data @@ -124,8 +124,8 @@ def test_403_page_check_works_with_ignore_status_code(client, live_server, measu # Give the thread time to pick it up wait_for_all_checks(client) - # It should have 'unviewed' still + # It should have 'has-unread-changes' still # Because it should be looking at only that 'sametext' id res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data diff --git a/changedetectionio/tests/test_ignorewhitespace.py b/changedetectionio/tests/test_ignorewhitespace.py index 93fa94b2..5a3f434c 100644 --- a/changedetectionio/tests/test_ignorewhitespace.py +++ b/changedetectionio/tests/test_ignorewhitespace.py @@ -89,7 +89,7 @@ def test_check_ignore_whitespace(client, live_server, measure_memory_usage): # Give the thread time to pick it up time.sleep(sleep_time_for_fetch_thread) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data diff --git a/changedetectionio/tests/test_jinja2.py b/changedetectionio/tests/test_jinja2.py index 6760faf1..e5a80714 100644 --- a/changedetectionio/tests/test_jinja2.py +++ b/changedetectionio/tests/test_jinja2.py @@ -26,7 +26,7 @@ def test_jinja2_in_url_query(client, live_server, measure_memory_usage): assert b"Watch added" in res.data wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get( url_for("ui.ui_views.preview_page", uuid="first"), follow_redirects=True @@ -51,7 +51,7 @@ def test_jinja2_security_url_query(client, live_server, measure_memory_usage): assert b"Watch added" in res.data wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) assert b'is invalid and cannot be used' in res.data # Some of the spewed output from the subclasses diff --git a/changedetectionio/tests/test_jsonpath_jq_selector.py b/changedetectionio/tests/test_jsonpath_jq_selector.py index b33da74d..217e623d 100644 --- a/changedetectionio/tests/test_jsonpath_jq_selector.py +++ b/changedetectionio/tests/test_jsonpath_jq_selector.py @@ -280,9 +280,9 @@ def check_json_filter(json_filter, client, live_server): # Give the thread time to pick it up wait_for_all_checks(client) - # It should have 'unviewed' still + # It should have 'has-unread-changes' still res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Should not see this, because its not in the JSONPath we entered res = client.get(url_for("ui.ui_views.diff_history_page", uuid="first")) @@ -418,14 +418,14 @@ def check_json_ext_filter(json_filter, client, live_server): # Give the thread time to pick it up wait_for_all_checks(client) - # It should have 'unviewed' + # It should have 'has-unread-changes' res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.ui_views.preview_page", uuid="first")) # We should never see 'ForSale' because we are selecting on 'Sold' in the rule, - # But we should know it triggered ('unviewed' assert above) + # But we should know it triggered ('has-unread-changes' assert above) assert b'ForSale' not in res.data assert b'Sold' in res.data @@ -465,7 +465,7 @@ def test_ignore_json_order(client, live_server, measure_memory_usage): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Just to be sure it still works with open("test-datastore/endpoint-content.txt", "w") as f: @@ -476,7 +476,7 @@ def test_ignore_json_order(client, live_server, measure_memory_usage): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data diff --git a/changedetectionio/tests/test_nonrenderable_pages.py b/changedetectionio/tests/test_nonrenderable_pages.py index 2b3bd305..70d0c2e9 100644 --- a/changedetectionio/tests/test_nonrenderable_pages.py +++ b/changedetectionio/tests/test_nonrenderable_pages.py @@ -40,9 +40,9 @@ def test_check_basic_change_detection_functionality(client, live_server, measure wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data ##################### @@ -62,9 +62,9 @@ def test_check_basic_change_detection_functionality(client, live_server, measure # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data uuid = next(iter(live_server.app.config['DATASTORE'].data['watching'])) watch = live_server.app.config['DATASTORE'].data['watching'][uuid] @@ -92,9 +92,9 @@ def test_check_basic_change_detection_functionality(client, live_server, measure # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data client.get(url_for("ui.mark_all_viewed"), follow_redirects=True) time.sleep(0.2) @@ -108,7 +108,7 @@ def test_check_basic_change_detection_functionality(client, live_server, measure wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data # A change should have registered because empty_pages_are_a_change is ON + assert b'has-unread-changes' in res.data # A change should have registered because empty_pages_are_a_change is ON assert b'fetch-error' not in res.data # diff --git a/changedetectionio/tests/test_pdf.py b/changedetectionio/tests/test_pdf.py index 5a8080b2..90da876a 100644 --- a/changedetectionio/tests/test_pdf.py +++ b/changedetectionio/tests/test_pdf.py @@ -49,9 +49,9 @@ def test_fetch_pdf(client, live_server, measure_memory_usage): wait_for_all_checks(client) - # Now something should be ready, indicated by having a 'unviewed' class + # Now something should be ready, indicated by having a 'has-unread-changes' class res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # The original checksum should be not be here anymore (cdio adds it to the bottom of the text) diff --git a/changedetectionio/tests/test_preview_endpoints.py b/changedetectionio/tests/test_preview_endpoints.py index ada52ed4..bc4d5412 100644 --- a/changedetectionio/tests/test_preview_endpoints.py +++ b/changedetectionio/tests/test_preview_endpoints.py @@ -47,9 +47,9 @@ def test_fetch_pdf(client, live_server, measure_memory_usage): wait_for_all_checks(client) - # Now something should be ready, indicated by having a 'unviewed' class + # Now something should be ready, indicated by having a 'has-unread-changes' class res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # The original checksum should be not be here anymore (cdio adds it to the bottom of the text) diff --git a/changedetectionio/tests/test_restock_itemprop.py b/changedetectionio/tests/test_restock_itemprop.py index 9efd6f1b..6cc3a2b2 100644 --- a/changedetectionio/tests/test_restock_itemprop.py +++ b/changedetectionio/tests/test_restock_itemprop.py @@ -112,7 +112,7 @@ def test_itemprop_price_change(client, live_server): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'180.45' in res.data - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data client.get(url_for("ui.mark_all_viewed"), follow_redirects=True) time.sleep(0.2) @@ -129,7 +129,7 @@ def test_itemprop_price_change(client, live_server): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'120.45' in res.data - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) @@ -178,7 +178,7 @@ def _run_test_minmax_limit(client, extra_watch_edit_form): assert b'more than one price detected' not in res.data # BUT the new price should show, even tho its within limits assert b'1,000.45' or b'1000.45' in res.data #depending on locale - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # price changed to something LESS than min (900), SHOULD be a change set_original_response(props_markup=instock_props[0], price='890.45') @@ -188,7 +188,7 @@ def _run_test_minmax_limit(client, extra_watch_edit_form): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'890.45' in res.data - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data client.get(url_for("ui.mark_all_viewed")) @@ -200,7 +200,7 @@ def _run_test_minmax_limit(client, extra_watch_edit_form): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'820.45' in res.data - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data client.get(url_for("ui.mark_all_viewed")) # price changed to something MORE than max (1100.10), SHOULD be a change @@ -210,7 +210,7 @@ def _run_test_minmax_limit(client, extra_watch_edit_form): res = client.get(url_for("watchlist.index")) # Depending on the LOCALE it may be either of these (generally for US/default/etc) assert b'1,890.45' in res.data or b'1890.45' in res.data - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data @@ -294,7 +294,7 @@ def test_itemprop_percent_threshold(client, live_server): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'960.45' in res.data - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Bigger INCREASE change than the threshold should trigger set_original_response(props_markup=instock_props[0], price='1960.45') @@ -302,7 +302,7 @@ def test_itemprop_percent_threshold(client, live_server): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'1,960.45' or b'1960.45' in res.data #depending on locale - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Small decrease should NOT trigger @@ -312,7 +312,7 @@ def test_itemprop_percent_threshold(client, live_server): wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'1,950.45' or b'1950.45' in res.data #depending on locale - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data diff --git a/changedetectionio/tests/test_source.py b/changedetectionio/tests/test_source.py index bea310b4..4aeb0f9e 100644 --- a/changedetectionio/tests/test_source.py +++ b/changedetectionio/tests/test_source.py @@ -43,9 +43,9 @@ def test_check_basic_change_detection_functionality_source(client, live_server, wait_for_all_checks(client) - # Now something should be ready, indicated by having a 'unviewed' class + # Now something should be ready, indicated by having a 'has-unread-changes' class res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get( url_for("ui.ui_views.diff_history_page", uuid="first"), diff --git a/changedetectionio/tests/test_trigger.py b/changedetectionio/tests/test_trigger.py index 6bccf812..8d30b9bb 100644 --- a/changedetectionio/tests/test_trigger.py +++ b/changedetectionio/tests/test_trigger.py @@ -96,7 +96,7 @@ def test_trigger_functionality(client, live_server, measure_memory_usage): - # so that we set the state to 'unviewed' after all the edits + # so that we set the state to 'has-unread-changes' after all the edits client.get(url_for("ui.ui_views.diff_history_page", uuid="first")) # Trigger a check @@ -104,9 +104,9 @@ def test_trigger_functionality(client, live_server, measure_memory_usage): wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data assert b'/test-endpoint' in res.data # Make a change @@ -116,9 +116,9 @@ def test_trigger_functionality(client, live_server, measure_memory_usage): client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Now set the content which contains the trigger text set_modified_with_trigger_text_response() @@ -126,7 +126,7 @@ def test_trigger_functionality(client, live_server, measure_memory_usage): client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # https://github.com/dgtlmoon/changedetection.io/issues/616 # Apparently the actual snapshot that contains the trigger never shows diff --git a/changedetectionio/tests/test_trigger_regex.py b/changedetectionio/tests/test_trigger_regex.py index 987ef063..aa22ac8a 100644 --- a/changedetectionio/tests/test_trigger_regex.py +++ b/changedetectionio/tests/test_trigger_regex.py @@ -42,7 +42,7 @@ def test_trigger_regex_functionality(client, live_server, measure_memory_usage): # It should report nothing found (just a new one shouldnt have anything) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data ### test regex res = client.post( @@ -54,7 +54,7 @@ def test_trigger_regex_functionality(client, live_server, measure_memory_usage): follow_redirects=True ) wait_for_all_checks(client) - # so that we set the state to 'unviewed' after all the edits + # so that we set the state to 'has-unread-changes' after all the edits client.get(url_for("ui.ui_views.diff_history_page", uuid="first")) with open("test-datastore/endpoint-content.txt", "w") as f: @@ -65,7 +65,7 @@ def test_trigger_regex_functionality(client, live_server, measure_memory_usage): # It should report nothing found (nothing should match the regex) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data with open("test-datastore/endpoint-content.txt", "w") as f: f.write("regex test123
\nsomething 123") @@ -73,7 +73,7 @@ def test_trigger_regex_functionality(client, live_server, measure_memory_usage): client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Cleanup everything res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) diff --git a/changedetectionio/tests/test_trigger_regex_with_filter.py b/changedetectionio/tests/test_trigger_regex_with_filter.py index b6816526..43f90c25 100644 --- a/changedetectionio/tests/test_trigger_regex_with_filter.py +++ b/changedetectionio/tests/test_trigger_regex_with_filter.py @@ -69,7 +69,7 @@ def test_trigger_regex_functionality_with_filter(client, live_server, measure_me # It should report nothing found (nothing should match the regex and filter) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # now this should trigger something with open("test-datastore/endpoint-content.txt", "w") as f: @@ -78,7 +78,7 @@ def test_trigger_regex_functionality_with_filter(client, live_server, measure_me client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) time.sleep(sleep_time_for_fetch_thread) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data # Cleanup everything res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) diff --git a/changedetectionio/tests/test_ui.py b/changedetectionio/tests/test_ui.py index 5c9ee51e..14e1d9ab 100644 --- a/changedetectionio/tests/test_ui.py +++ b/changedetectionio/tests/test_ui.py @@ -248,3 +248,44 @@ def test_page_title_listing_behaviour(client, live_server): res = client.get(url_for("watchlist.index")) assert b"head titlecustom html" in res.data + +def test_ui_viewed_unread_flag(client, live_server): + + import time + + set_original_response(extra_title="custom html") + + # Add our URL to the import page + res = client.post( + url_for("imports.import_page"), + data={"urls": url_for('test_endpoint', _external=True)+"\r\n"+url_for('test_endpoint', _external=True)}, + follow_redirects=True + ) + + assert b"2 Imported" in res.data + wait_for_all_checks(client) + + set_modified_response() + res = client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) + assert b'Queued 2 watches for rechecking.' in res.data + wait_for_all_checks(client) + res = client.get(url_for("watchlist.index")) + assert b'2' in res.data + assert res.data.count(b'data-watch-uuid') == 2 + + # one should now be viewed, but two in total still + client.get(url_for("ui.ui_views.diff_history_page", uuid="first")) + res = client.get(url_for("watchlist.index")) + assert b'1' in res.data + assert res.data.count(b'data-watch-uuid') == 2 + + # check ?unread=1 works + res = client.get(url_for("watchlist.index")+"?unread=1") + assert res.data.count(b'data-watch-uuid') == 1 + assert b'1' in res.data + + # Mark all viewed test again + client.get(url_for("ui.mark_all_viewed"), follow_redirects=True) + time.sleep(0.2) + res = client.get(url_for("watchlist.index")) + assert b'0' in res.data \ No newline at end of file diff --git a/changedetectionio/tests/test_unique_lines.py b/changedetectionio/tests/test_unique_lines.py index 91425cb7..de8d7b96 100644 --- a/changedetectionio/tests/test_unique_lines.py +++ b/changedetectionio/tests/test_unique_lines.py @@ -97,7 +97,7 @@ def test_unique_lines_functionality(client, live_server, measure_memory_usage): follow_redirects=True ) assert b"Updated watch." in res.data - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Make a change set_modified_swapped_lines() @@ -108,16 +108,16 @@ def test_unique_lines_functionality(client, live_server, measure_memory_usage): # Give the thread time to pick it up wait_for_all_checks(client) - # It should report nothing found (no new 'unviewed' class) + # It should report nothing found (no new 'has-unread-changes' class) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data # Now set the content which contains the new text and re-ordered existing text set_modified_with_trigger_text_response() client.get(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data @@ -157,7 +157,7 @@ def test_sort_lines_functionality(client, live_server, measure_memory_usage): res = client.get(url_for("watchlist.index")) # Should be a change registered - assert b'unviewed' in res.data + assert b'has-unread-changes' in res.data res = client.get( url_for("ui.ui_views.preview_page", uuid="first"), diff --git a/changedetectionio/tests/test_xpath_selector.py b/changedetectionio/tests/test_xpath_selector.py index a1d203c2..d79fa4b3 100644 --- a/changedetectionio/tests/test_xpath_selector.py +++ b/changedetectionio/tests/test_xpath_selector.py @@ -208,7 +208,7 @@ def test_check_markup_xpath_filter_restriction(client, live_server, measure_memo wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) - assert b'unviewed' not in res.data + assert b'has-unread-changes' not in res.data res = client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True) assert b'Deleted' in res.data diff --git a/requirements.txt b/requirements.txt index cb220097..d448f7aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -39,7 +39,7 @@ jsonpath-ng~=1.5.3 # jq not available on Windows so must be installed manually # Notification library -apprise==1.9.3 +apprise==1.9.5 # - Needed for apprise/spush, and maybe others? hopefully doesnt trigger a rust compile. # - Requires extra wheel for rPi, adds build time for arm/v8 which is not in piwheels @@ -51,8 +51,8 @@ cryptography==44.0.1 # use any version other than 2.0.x due to https://github.com/eclipse/paho.mqtt.python/issues/814 paho-mqtt!=2.0.* -# Used for CSS filtering -beautifulsoup4>=4.0.0 +# Used for CSS filtering, JSON extraction from HTML +beautifulsoup4>=4.0.0,<=4.13.5 # XPath filtering, lxml is required by bs4 anyway, but put it here to be safe. # #2328 - 5.2.0 and 5.2.1 had extra CPU flag CFLAGS set which was not compatible on older hardware