diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index 6ab499b..31397bf 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -459,7 +459,7 @@ function buildTileHTML(session, index, mobile) { `${badgeHtml}${badgeHtml ? `\xb7` : ''}${escapeHtml(timeStr)}` + `` + `
${ansiToHtml(lastLines)}
` + - `` + + `` + `` ); } diff --git a/muxplex/tests/test_frontend_js.py b/muxplex/tests/test_frontend_js.py index 6a79c23..ff3d0b0 100644 --- a/muxplex/tests/test_frontend_js.py +++ b/muxplex/tests/test_frontend_js.py @@ -1273,7 +1273,6 @@ def test_bind_static_event_listeners_binds_auto_open_change() -> None: ) - def test_exports_load_server_settings() -> None: """module.exports must export loadServerSettings.""" match = re.search( @@ -2974,7 +2973,6 @@ def test_create_device_select_uses_device_id_for_option_value() -> None: ) - # --------------------------------------------------------------------------- # Active view state variable and getVisibleSessions (task-2-active-view) # --------------------------------------------------------------------------- @@ -3282,6 +3280,7 @@ def test_render_view_dropdown_no_single_hyphen_item_class() -> None: body = match.group(1) # Should not have the old single-hyphen class; allow only '--active' modifiers via '__' import re as _re + bad_matches = _re.findall(r'"view-dropdown-item(?!--active)', body) assert not bad_matches, ( "renderViewDropdown must not use single-hyphen 'view-dropdown-item' class (use BEM 'view-dropdown__item')" @@ -3357,6 +3356,7 @@ def test_render_sidebar_view_dropdown_no_single_hyphen_item_class() -> None: assert match, "renderSidebarViewDropdown function not found" body = match.group(1) import re as _re + bad_matches = _re.findall(r'"view-dropdown-item(?!--active)', body) assert not bad_matches, ( "renderSidebarViewDropdown must not use single-hyphen 'view-dropdown-item' class" @@ -3393,7 +3393,7 @@ def test_render_view_dropdown_buttons_have_role_menuitem() -> None: assert match, "renderViewDropdown function not found" body = match.group(1) assert 'role="menuitem"' in body, ( - "renderViewDropdown must include role=\"menuitem\" on buttons — " + 'renderViewDropdown must include role="menuitem" on buttons — ' "handleGlobalKeydown arrow navigation queries [role='menuitem']" ) @@ -3408,7 +3408,7 @@ def test_render_sidebar_view_dropdown_buttons_have_role_menuitem() -> None: assert match, "renderSidebarViewDropdown function not found" body = match.group(1) assert 'role="menuitem"' in body, ( - "renderSidebarViewDropdown must include role=\"menuitem\" on buttons" + 'renderSidebarViewDropdown must include role="menuitem" on buttons' ) @@ -3444,7 +3444,9 @@ def test_manage_views_action_opens_settings() -> None: body = match.group(1) # The manage-views action must do more than just close the dropdown # Find the section near 'manage-views' - assert "manage-views" in body, "bindStaticEventListeners must handle manage-views action" + assert "manage-views" in body, ( + "bindStaticEventListeners must handle manage-views action" + ) # After manage-views, openSettings must be called idx = body.find("manage-views") nearby = body[idx : idx + 200] @@ -3487,7 +3489,9 @@ def test_delete_active_view_persists_active_view_to_server() -> None: body = match.group(1) # Find the section that sets _activeView = 'all' (the delete fallback path) fallback_idx = body.find("_activeView = 'all'") - assert fallback_idx >= 0, "_activeView = 'all' fallback not found in renderViewsSettingsTab" + assert fallback_idx >= 0, ( + "_activeView = 'all' fallback not found in renderViewsSettingsTab" + ) # Within ~200 chars after the fallback, there must be an api( call for persistence nearby = body[fallback_idx : fallback_idx + 200] assert "api(" in nearby, ( @@ -3675,3 +3679,33 @@ def test_bind_static_event_listeners_no_filter_bar_click_handler() -> None: "bindStaticEventListeners must not contain the dead filter-bar click handler " "that sets _activeFilterDevice" ) + + +# --------------------------------------------------------------------------- +# Tile options button (⋮) replaces tile-delete +# --------------------------------------------------------------------------- + + +def test_tile_has_options_button() -> None: + """buildTileHTML must include a .tile-options-btn button.""" + fn_body = _JS.split("function buildTileHTML")[1].split("\nfunction ")[0] + assert "tile-options-btn" in fn_body, ( + "buildTileHTML must render a .tile-options-btn element" + ) + + +def test_tile_options_btn_has_aria() -> None: + """The ⋮ button must have aria-label='Session options' and aria-haspopup='true'.""" + fn_body = _JS.split("function buildTileHTML")[1].split("\nfunction ")[0] + assert "aria-label" in fn_body and "Session options" in fn_body, ( + "tile-options-btn must have aria-label='Session options'" + ) + assert "aria-haspopup" in fn_body, "tile-options-btn must have aria-haspopup='true'" + + +def test_tile_delete_button_removed() -> None: + """buildTileHTML must NOT include the old .tile-delete button.""" + fn_body = _JS.split("function buildTileHTML")[1].split("\nfunction ")[0] + assert "tile-delete" not in fn_body, ( + "buildTileHTML must not render the old .tile-delete button (kill moved to flyout)" + )