feat: rewrite getVisibleSessions() to honor active view (task-2)
- Add _activeView state variable with default 'all' in App state section - Rewrite getVisibleSessions() to filter by active view: - 'all' view: excludes hidden sessions - 'hidden' view: shows only hidden sessions - user view: filters to view's sessions list using sessionKey - Falls back to 'all' if active view no longer exists - Add _getActiveView() and _setActiveView() test helpers - Export new test helpers in window.MuxplexApp (module.exports) - Add 6 new tests covering all acceptance criteria Tests added: - test_active_view_state_variable_exists - test_get_visible_sessions_all_view_excludes_hidden - test_get_visible_sessions_hidden_view_shows_hidden - test_get_visible_sessions_user_view_filters_by_session_key - test_get_active_view_helper_exported - test_set_active_view_helper_exported All 220 tests pass.
This commit is contained in:
+41
-5
@@ -137,6 +137,7 @@ let _settingsOpen = false;
|
|||||||
let _serverSettings = null;
|
let _serverSettings = null;
|
||||||
let _gridViewMode = 'flat';
|
let _gridViewMode = 'flat';
|
||||||
let _activeFilterDevice = 'all';
|
let _activeFilterDevice = 'all';
|
||||||
|
let _activeView = 'all';
|
||||||
const DISPLAY_DEFAULTS = {
|
const DISPLAY_DEFAULTS = {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
hoverPreviewDelay: 1500,
|
hoverPreviewDelay: 1500,
|
||||||
@@ -531,18 +532,45 @@ function buildStatusTileHTML(deviceName, statusText, statusClass) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns sessions with hidden session names removed.
|
* Returns sessions filtered by the active view.
|
||||||
* Hides any session whose name appears in the hidden_sessions list,
|
*
|
||||||
* regardless of whether it is local or remote (federated).
|
* - 'all' view: excludes hidden sessions (sessions in hidden_sessions list)
|
||||||
* Consolidates the hidden-session filter used by all render paths.
|
* - 'hidden' view: shows only hidden sessions
|
||||||
|
* - user view: shows only sessions whose sessionKey is in that view's sessions list
|
||||||
|
*
|
||||||
|
* Status entries (unreachable, auth_failed, empty) are always excluded —
|
||||||
|
* they are rendered separately as status tiles.
|
||||||
|
*
|
||||||
|
* Falls back to 'all' behaviour if the active view no longer exists.
|
||||||
|
*
|
||||||
* @param {object[]} sessions
|
* @param {object[]} sessions
|
||||||
* @returns {object[]}
|
* @returns {object[]}
|
||||||
*/
|
*/
|
||||||
function getVisibleSessions(sessions) {
|
function getVisibleSessions(sessions) {
|
||||||
var hidden = (_serverSettings && _serverSettings.hidden_sessions) || [];
|
var hidden = (_serverSettings && _serverSettings.hidden_sessions) || [];
|
||||||
|
var views = (_serverSettings && _serverSettings.views) || [];
|
||||||
|
var view = _resolveActiveView(_activeView, views);
|
||||||
|
|
||||||
return (sessions || []).filter(function(s) {
|
return (sessions || []).filter(function(s) {
|
||||||
// Skip status entries (unreachable, auth_failed) — rendered separately as status tiles
|
// Skip status entries (unreachable, auth_failed, empty) — rendered separately as status tiles
|
||||||
if (s.status) return false;
|
if (s.status) return false;
|
||||||
|
|
||||||
|
if (view === 'hidden') {
|
||||||
|
// 'hidden' view: only show sessions that are in the hidden list
|
||||||
|
return hidden.length > 0 && (hidden.includes(s.sessionKey || s.name) || hidden.includes(s.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view !== 'all') {
|
||||||
|
// User-defined view: show only sessions whose sessionKey is in this view's sessions list
|
||||||
|
var userView = views.find(function(v) { return v.name === view; });
|
||||||
|
if (userView) {
|
||||||
|
var viewSessions = userView.sessions || [];
|
||||||
|
return viewSessions.includes(s.sessionKey || s.name) || viewSessions.includes(s.name);
|
||||||
|
}
|
||||||
|
// View no longer exists — fall through to 'all' behaviour
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'all' view: exclude hidden sessions
|
||||||
if (hidden.length > 0 && (hidden.includes(s.sessionKey || s.name) || hidden.includes(s.name))) {
|
if (hidden.length > 0 && (hidden.includes(s.sessionKey || s.name) || hidden.includes(s.name))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2499,6 +2527,12 @@ function _setActiveFilterDevice(device) {
|
|||||||
_activeFilterDevice = device;
|
_activeFilterDevice = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Test-only: get current _activeView value. */
|
||||||
|
function _getActiveView() { return _activeView; }
|
||||||
|
|
||||||
|
/** Test-only: set _activeView directly. */
|
||||||
|
function _setActiveView(view) { _activeView = view; }
|
||||||
|
|
||||||
// Recalculate fit layout on window resize
|
// Recalculate fit layout on window resize
|
||||||
window.addEventListener('resize', function() {
|
window.addEventListener('resize', function() {
|
||||||
var ds = getDisplaySettings();
|
var ds = getDisplaySettings();
|
||||||
@@ -2625,5 +2659,7 @@ if (typeof module !== 'undefined' && module.exports) {
|
|||||||
_getGridViewMode,
|
_getGridViewMode,
|
||||||
_setGridViewMode,
|
_setGridViewMode,
|
||||||
_setActiveFilterDevice,
|
_setActiveFilterDevice,
|
||||||
|
_getActiveView,
|
||||||
|
_setActiveView,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3010,3 +3010,86 @@ def test_settings_hidden_sessions_uses_session_key() -> None:
|
|||||||
"(with s.name fallback) as the checkbox value so remote sessions are "
|
"(with s.name fallback) as the checkbox value so remote sessions are "
|
||||||
"stored in device_id:name format in hidden_sessions"
|
"stored in device_id:name format in hidden_sessions"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Active view state variable and getVisibleSessions (task-2-active-view)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def test_active_view_state_variable_exists() -> None:
|
||||||
|
"""_activeView state variable must be declared with default 'all'."""
|
||||||
|
assert "let _activeView = 'all'" in _JS, (
|
||||||
|
"let _activeView = 'all' must be declared in the App state section of app.js"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_visible_sessions_all_view_excludes_hidden() -> None:
|
||||||
|
"""getVisibleSessions must reference _activeView to honour the active view."""
|
||||||
|
match = re.search(
|
||||||
|
r"function getVisibleSessions\(sessions\)\s*\{(.*?)\n\}",
|
||||||
|
_JS,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
assert match, "getVisibleSessions function not found"
|
||||||
|
body = match.group(1)
|
||||||
|
assert "_activeView" in body, (
|
||||||
|
"getVisibleSessions must check _activeView to filter sessions by view"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_visible_sessions_hidden_view_shows_hidden() -> None:
|
||||||
|
"""getVisibleSessions must handle the 'hidden' view case."""
|
||||||
|
match = re.search(
|
||||||
|
r"function getVisibleSessions\(sessions\)\s*\{(.*?)\n\}",
|
||||||
|
_JS,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
assert match, "getVisibleSessions function not found"
|
||||||
|
body = match.group(1)
|
||||||
|
assert "'hidden'" in body or '"hidden"' in body, (
|
||||||
|
"getVisibleSessions must explicitly handle the 'hidden' view (show only hidden sessions)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_visible_sessions_user_view_filters_by_session_key() -> None:
|
||||||
|
"""getVisibleSessions must use sessionKey when filtering for a user-defined view."""
|
||||||
|
match = re.search(
|
||||||
|
r"function getVisibleSessions\(sessions\)\s*\{(.*?)\n\}",
|
||||||
|
_JS,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
assert match, "getVisibleSessions function not found"
|
||||||
|
body = match.group(1)
|
||||||
|
assert "sessionKey" in body, (
|
||||||
|
"getVisibleSessions must use sessionKey to match sessions against the user view's "
|
||||||
|
"sessions list"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_active_view_helper_exported() -> None:
|
||||||
|
"""window.MuxplexApp must export _getActiveView test helper."""
|
||||||
|
match = re.search(
|
||||||
|
r"module\.exports\s*=\s*\{(.*?)\};",
|
||||||
|
_JS,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
assert match, "module.exports block not found"
|
||||||
|
exports = match.group(1)
|
||||||
|
assert "_getActiveView" in exports, (
|
||||||
|
"module.exports must export _getActiveView test helper"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_active_view_helper_exported() -> None:
|
||||||
|
"""window.MuxplexApp must export _setActiveView test helper."""
|
||||||
|
match = re.search(
|
||||||
|
r"module\.exports\s*=\s*\{(.*?)\};",
|
||||||
|
_JS,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
assert match, "module.exports block not found"
|
||||||
|
exports = match.group(1)
|
||||||
|
assert "_setActiveView" in exports, (
|
||||||
|
"module.exports must export _setActiveView test helper"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user