#!/usr/bin/env python3 """Re #4021 - the "Mark all viewed" button must be driven by live state, not by render-time presence. The button used to be wrapped in {% if unread_count %}, so when nothing was unread it did not exist in the DOM at all. A second tab could therefore never show it after a change arrived, and never hide it after another tab marked everything viewed - no socket event can style an element that isn't there. It is now always rendered and revealed by body.has-any-unviewed, which is set server-side via extra_classes and kept live by the general_stats_update socket event. These assertions pin both halves: the element is always present, and the body class tracks the unviewed state. """ from flask import url_for from .util import set_original_response, set_modified_response, wait_for_all_checks, delete_all_watches def _body_has_any_unviewed(res): """The class list is what CSS keys off - realtime.js toggles the same class.""" for line in res.data.decode('utf-8').split('\n'): if ' found in response") def test_mark_all_viewed_button_is_always_in_the_dom(client, live_server, measure_memory_usage, datastore_path): set_original_response(datastore_path=datastore_path) client.post( url_for("ui.ui_views.form_quick_watch_add"), data={"url": url_for('test_endpoint', _external=True), "tags": ''}, follow_redirects=True ) wait_for_all_checks(client) # First snapshot only - nothing is unread yet res = client.get(url_for("watchlist.index")) assert b'id="post-list-mark-views"' in res.data, \ "Button must be in the DOM even with nothing unread, or another tab can never reveal it" assert not _body_has_any_unviewed(res), "Nothing unread, so the body class must be absent" # A real change lands -> unviewed set_modified_response(datastore_path=datastore_path) client.post(url_for("ui.form_watch_checknow"), follow_redirects=True) wait_for_all_checks(client) res = client.get(url_for("watchlist.index")) assert b'id="post-list-mark-views"' in res.data assert _body_has_any_unviewed(res), "Unviewed change present, so the body class must be set" # Mark all viewed runs synchronously (Re #4021), so the redirect must already reflect it res = client.post(url_for("ui.mark_all_viewed"), follow_redirects=True) assert not _body_has_any_unviewed(res), \ "mark_all_viewed must be applied before the redirect renders - it used to run in a " \ "background thread and the page rendered mid-marking" assert b'id="post-list-mark-views"' in res.data, "Still in the DOM, just hidden by CSS" assert b'class="has-unread-changes' not in res.data delete_all_watches(client)