Voor remote client agent

This commit is contained in:
kodi
2026-03-25 18:21:54 +01:00
parent 9537a29de3
commit fc4ec39646
14 changed files with 1892 additions and 14 deletions
Binary file not shown.
@@ -259,13 +259,23 @@ class UiSmokeGoldenTest(unittest.TestCase):
self._extract_js_function(app_js, "formatTaskLine"),
self._extract_js_function(app_js, "isActiveTask"),
self._extract_js_function(app_js, "activeTasksFromItems"),
self._extract_js_function(app_js, "isTerminalOperationTask"),
self._extract_js_function(app_js, "statusBadgeLabel"),
self._extract_js_function(app_js, "terminalOperationChipLabel"),
self._extract_js_function(app_js, "visibleOperationSortKey"),
self._extract_js_function(app_js, "sortVisibleOperations"),
self._extract_js_function(app_js, "taskIsCancellable"),
self._extract_js_function(app_js, "cancelTaskRequest"),
self._extract_js_function(app_js, "formatTaskOperationLabel"),
self._extract_js_function(app_js, "statusBadgeLabel"),
self._extract_js_function(app_js, "hasMeaningfulItemProgress"),
self._extract_js_function(app_js, "canShowChipItemProgress"),
self._extract_js_function(app_js, "compactTaskCurrentItem"),
self._extract_js_function(app_js, "activeTaskChipLabel"),
self._extract_js_function(app_js, "isTerminalOperationTask"),
self._extract_js_function(app_js, "terminalOperationChipLabel"),
self._extract_js_function(app_js, "visibleOperationSortKey"),
self._extract_js_function(app_js, "sortVisibleOperations"),
self._extract_js_function(app_js, "taskProgressText"),
self._extract_js_function(app_js, "taskProgressSubtext"),
self._extract_js_function(app_js, "headerTaskRenderKey"),
@@ -360,16 +370,23 @@ class UiSmokeGoldenTest(unittest.TestCase):
}}
async function refreshTasksSnapshot() {{}}
async function loadBrowsePane() {{}}
function setError() {{}}
let headerTaskState = {{
activeItems: [],
visibleItems: [],
recentItems: [],
popoverOpen: false,
pollTimer: null,
lastRenderKey: "",
knownStatuses: {{}},
recentExpiryMs: 4000,
paneRefreshPromise: null,
}};
const ACTIVE_OPERATION_OPERATIONS = new Set(["copy", "move", "duplicate", "delete"]);
const ACTIVE_TASK_STATUSES = new Set(["queued", "running", "cancelling"]);
const TERMINAL_OPERATION_STATUSES = new Set(["completed", "cancelled", "failed"]);
{functions}
@@ -412,8 +429,10 @@ class UiSmokeGoldenTest(unittest.TestCase):
const deleteRow = elements["header-task-popover-list"].children[3];
const deleteProgress = deleteRow.children[3];
const deleteCurrent = deleteRow.children[4];
const deleteBadge = deleteRow.children[0].children[1];
assert(deleteProgress.textContent === "2/5", "Delete operations should show done/total progress when available");
assert(deleteCurrent.textContent === "folder/delete-me.txt", "Delete operations should show compact current item");
assert(deleteBadge.textContent === "Running", "Delete operations should expose a readable status badge");
const cancellingRow = elements["header-task-popover-list"].children[4];
const cancellingProgress = cancellingRow.children[3];
const cancellingCurrent = cancellingRow.children[4];
@@ -485,10 +504,15 @@ class UiSmokeGoldenTest(unittest.TestCase):
self._extract_js_function(app_js, "taskIsCancellable"),
self._extract_js_function(app_js, "cancelTaskRequest"),
self._extract_js_function(app_js, "formatTaskOperationLabel"),
self._extract_js_function(app_js, "statusBadgeLabel"),
self._extract_js_function(app_js, "hasMeaningfulItemProgress"),
self._extract_js_function(app_js, "canShowChipItemProgress"),
self._extract_js_function(app_js, "compactTaskCurrentItem"),
self._extract_js_function(app_js, "activeTaskChipLabel"),
self._extract_js_function(app_js, "isTerminalOperationTask"),
self._extract_js_function(app_js, "terminalOperationChipLabel"),
self._extract_js_function(app_js, "visibleOperationSortKey"),
self._extract_js_function(app_js, "sortVisibleOperations"),
self._extract_js_function(app_js, "taskProgressText"),
self._extract_js_function(app_js, "taskProgressSubtext"),
self._extract_js_function(app_js, "headerTaskRenderKey"),
@@ -499,6 +523,7 @@ class UiSmokeGoldenTest(unittest.TestCase):
self._extract_js_function(app_js, "renderHeaderTaskPopover"),
self._extract_js_function(app_js, "renderHeaderTaskChip"),
self._extract_js_function(app_js, "updateHeaderTaskState"),
self._extract_js_function(app_js, "refreshOperationPanes"),
self._extract_js_function(app_js, "applyTaskSnapshot"),
]
)
@@ -583,17 +608,25 @@ class UiSmokeGoldenTest(unittest.TestCase):
}}
async function refreshTasksSnapshot() {{}}
const paneRefreshCalls = [];
async function loadBrowsePane(pane) {{ paneRefreshCalls.push(pane); }}
function setError() {{}}
let state = {{ lastTaskCount: 0 }};
let headerTaskState = {{
activeItems: [],
visibleItems: [],
recentItems: [],
popoverOpen: false,
pollTimer: null,
lastRenderKey: "",
knownStatuses: {{}},
recentExpiryMs: 4000,
paneRefreshPromise: null,
}};
const ACTIVE_OPERATION_OPERATIONS = new Set(["copy", "move", "duplicate", "delete"]);
const ACTIVE_TASK_STATUSES = new Set(["queued", "running", "cancelling"]);
const TERMINAL_OPERATION_STATUSES = new Set(["completed", "cancelled", "failed"]);
{functions}
@@ -603,12 +636,16 @@ class UiSmokeGoldenTest(unittest.TestCase):
assert(!elements["header-task-chip-container"].classList.contains("hidden"), "Running task should make chip visible");
assert(elements["header-task-chip-label"].textContent === "Copy running", "Single active task should show compact task status");
assert(headerTaskState.activeItems.length === 1, "Snapshot should store active task state");
assert(paneRefreshCalls.length === 0, "Running progress should not refresh panes");
applyTaskSnapshot([
{{ id: "copy-1", operation: "copy", status: "completed", source: "/src", destination: "/dst" }},
]);
assert(elements["header-task-chip-container"].classList.contains("hidden"), "Chip should hide when latest task snapshot has no active tasks");
assert(!elements["header-task-chip-container"].classList.contains("hidden"), "Terminal operations should remain briefly visible");
assert(headerTaskState.activeItems.length === 0, "Active task state should be reset when tasks are completed");
assert(headerTaskState.recentItems.length === 1, "Completed operations should stay briefly visible as recent");
assert(elements["header-task-chip-label"].textContent === "1 recent operation", "Chip should surface a brief recent-operation state");
assert(paneRefreshCalls.length === 2, "Terminal operation should refresh both visible panes once");
assert(state.lastTaskCount === 1, "Total task snapshot should still reflect fetched tasks list length");
"""
)
@@ -961,6 +998,8 @@ class UiSmokeGoldenTest(unittest.TestCase):
self.assertIn(".header-task-chip {", base_css)
self.assertIn(".header-task-popover {", base_css)
self.assertIn(".header-task-popover-list {", base_css)
self.assertIn(".header-task-item-heading {", base_css)
self.assertIn(".header-task-status-badge {", base_css)
self.assertIn("width: min(1180px, calc(100vw - 32px));", base_css)
self.assertIn(".settings-activity-grid {", base_css)
self.assertIn("grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);", base_css)
@@ -1006,6 +1045,10 @@ class UiSmokeGoldenTest(unittest.TestCase):
self.assertIn('function headerTaskElements()', app_js)
self.assertIn('function isActiveTask(task)', app_js)
self.assertIn('function activeTasksFromItems(items)', app_js)
self.assertIn('function isTerminalOperationTask(task)', app_js)
self.assertIn('function statusBadgeLabel(task)', app_js)
self.assertIn('function terminalOperationChipLabel(items)', app_js)
self.assertIn('function sortVisibleOperations(items)', app_js)
self.assertIn('function taskIsCancellable(task)', app_js)
self.assertIn('async function cancelTaskRequest(taskId)', app_js)
self.assertIn('function formatTaskOperationLabel(task)', app_js)
@@ -1021,15 +1064,18 @@ class UiSmokeGoldenTest(unittest.TestCase):
self.assertIn('function renderHeaderTaskPopover(items)', app_js)
self.assertIn('function renderHeaderTaskChip(items)', app_js)
self.assertIn('function updateHeaderTaskState(taskItems)', app_js)
self.assertIn('function refreshOperationPanes()', app_js)
self.assertIn('function applyTaskSnapshot(taskItems)', app_js)
self.assertIn('return `${count} active operation${count === 1 ? "" : "s"}`;', app_js)
self.assertIn('return `${count} recent operation${count === 1 ? "" : "s"}`;', app_js)
self.assertIn('return task.operation === "copy" || task.operation === "duplicate" || task.operation === "delete";', app_js)
self.assertIn('return `${action} ${task.done_items}/${task.total_items}`;', app_js)
self.assertIn('return `${action} running`;', app_js)
self.assertIn('return "Stopping after current item...";', app_js)
self.assertIn('ACTIVE_OPERATION_OPERATIONS.has(task.operation)', app_js)
self.assertIn('headerTaskState.activeItems = activeTasksFromItems(taskItems);', app_js)
self.assertIn('const open = Boolean(nextOpen) && headerTaskState.activeItems.length > 0;', app_js)
self.assertIn('headerTaskState.activeItems = activeTasksFromItems(items);', app_js)
self.assertIn('headerTaskState.visibleItems = sortVisibleOperations([...headerTaskState.activeItems, ...headerTaskState.recentItems]);', app_js)
self.assertIn('const open = Boolean(nextOpen) && headerTaskState.visibleItems.length > 0;', app_js)
self.assertIn('const headerTasks = headerTaskElements();', app_js)
self.assertIn('headerTasks.chipButton.onclick = (event) => {', app_js)
self.assertIn('headerTasks.logsButton.onclick = () => {', app_js)