diff options
Diffstat (limited to 'packages/meshbay-hub/tests')
| -rw-r--r-- | packages/meshbay-hub/tests/test_app_settings_plugin.py | 72 | ||||
| -rw-r--r-- | packages/meshbay-hub/tests/test_upload_controls_hidden.py | 13 |
2 files changed, 79 insertions, 6 deletions
diff --git a/packages/meshbay-hub/tests/test_app_settings_plugin.py b/packages/meshbay-hub/tests/test_app_settings_plugin.py index 6a768a9..8a095af 100644 --- a/packages/meshbay-hub/tests/test_app_settings_plugin.py +++ b/packages/meshbay-hub/tests/test_app_settings_plugin.py @@ -320,3 +320,75 @@ def test_a_refusal_does_not_look_like_a_footnote(): assert "role=" in block and "alert" in block, ( "a refusal that appears after a click has to be announced, not just " "drawn") + + +# ── Saving, and being able to tell ────────────────────────────────────────── + +PANE_FLAGS = {"chat-app-settings.js": "dirty", + "video-app-settings.js": "dirsDirty", + "music-app-settings.js": "dirsDirty", + "photos-app-settings.js": "dirty"} + + +@pytest.mark.parametrize("pane", sorted(PANE_FLAGS)) +def test_save_is_a_button_and_not_dim_text(pane): + """ + It was `btn btn-small btn-secondary`, and there is no `.btn` rule in the + stylesheet at all — so it took `.btn-secondary`: no background, a + transparent border, dim grey text. Enabled it already looked like a + disabled control, and disabled it was that at 40% opacity. + + "You cannot always click Save, you do not notice, and it does not work" is + one sentence describing all of that. + """ + source = (STATIC / pane).read_text(encoding="utf-8") + assert 'class="app-save"' in source, f"{pane}'s Save is not the shared control" + assert "btn-secondary" not in source, ( + f"{pane}'s Save is still styled as dim text") + + +@pytest.mark.parametrize("pane", sorted(PANE_FLAGS)) +def test_an_inert_save_says_why(pane): + """ + A disabled control that explains itself is the difference between "nothing + changed" and "this is broken". Without it the reader has to know what the + pane counts as a change. + """ + source = (STATIC / pane).read_text(encoding="utf-8") + assert "app-save-why" in source and "settings_app.no_changes" in source + + +def test_the_disabled_state_is_visually_distinct(): + css = (STATIC / "style.css").read_text(encoding="utf-8") + rule = css[css.index(".app-save {"):] + rule = rule[:rule.index(".app-save-row")] + assert "var(--accent)" in rule, "an enabled Save has no fill" + disabled = rule[rule.index(".app-save:disabled"):] + assert "background: none" in disabled and "--text-dim" in disabled, ( + "disabled differs from enabled by opacity alone, which is what made " + "it unreadable") + + +def test_a_saved_setting_reaches_the_page_that_renders_the_pane(): + """ + Why Chat was the systematic case. + + `_dispatch` resolves an admin ack against the pending request and returns — + right for an op whose caller already knows the value it chose. Chat's pane + calls `transport.setChatDirectory` itself, so nothing told `group-page` + anything: the node saved it, every *other* connected client learned it from + the broadcast, and the one that asked went on showing an unsaved-looking + draft. Clicking Save again just re-sent it. + """ + transport = TRANSPORT.read_text(encoding="utf-8") + block = transport[transport.index("const BROADCAST_ACK_TYPES"):] + block = block[:block.index("]);") + 3] + for ack in ("chat_directory_ack", "chat_link_preview_ack", + "app_directories_ack"): + assert ack in block, f"{ack} is swallowed by its own request" + + assert "_replayBroadcast" in transport + replay = transport[transport.index("function _replayBroadcast"):] + replay = replay[:replay.index("\n}") + 2] + for cb in ("_onChatDirectory", "_onChatLinkPreview", "_onAppDirectories"): + assert cb in replay, f"{cb} is never called for the requester" diff --git a/packages/meshbay-hub/tests/test_upload_controls_hidden.py b/packages/meshbay-hub/tests/test_upload_controls_hidden.py index bdba373..f6f476e 100644 --- a/packages/meshbay-hub/tests/test_upload_controls_hidden.py +++ b/packages/meshbay-hub/tests/test_upload_controls_hidden.py @@ -212,16 +212,17 @@ def test_the_notice_also_answers_the_operators_own_request(): caused it. Every other admin ack can be resolved and dropped, because its caller - already knows what it asked for and updates local state from that. The root - acks carry a whole table only the node can compute — availability, the name - it settled on, the eject a failed plug left in place — so resolving one - without handing it on left the operator who clicked Eject as the only - client that never saw it happen. + already knows what it asked for and updates local state from that. These + are the ones the node *broadcasts*: every other connected client learns the + change from it, and the one that asked is the only one that does not, + because its own request swallowed its copy. Found on the root table, then + again on Chat's directory — where it meant the pane went on showing an + unsaved-looking draft after a save that had worked. """ transport = TRANSPORT.read_text(encoding="utf-8") block = transport[transport.index("msg.type.endsWith('_ack')"):] block = block[:block.index("_uploaders")] - assert "ROOT_ACK_TYPES" in block and "_onRootsChanged" in block, ( + assert "BROADCAST_ACK_TYPES" in block and "_replayBroadcast" in block, ( "the initiating client resolves the ack and learns nothing from it") |