feat: feedback verbetering 04
This commit is contained in:
@@ -40,6 +40,25 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
||||
return source[start : index + 1]
|
||||
self.fail(f"Expected closing brace for function {name}")
|
||||
|
||||
def _extract_async_js_function(self, source: str, name: str) -> str:
|
||||
marker = f"async function {name}("
|
||||
start = source.find(marker)
|
||||
if start < 0:
|
||||
self.fail(f"Expected async function {name} in app.js")
|
||||
brace_start = source.find("{", start)
|
||||
if brace_start < 0:
|
||||
self.fail(f"Expected opening brace for async function {name}")
|
||||
depth = 0
|
||||
for index in range(brace_start, len(source)):
|
||||
char = source[index]
|
||||
if char == "{":
|
||||
depth += 1
|
||||
elif char == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return source[start : index + 1]
|
||||
self.fail(f"Expected closing brace for async function {name}")
|
||||
|
||||
def _run_app_js_behavior_check(self, app_js: str) -> None:
|
||||
functions = "\n\n".join(
|
||||
[
|
||||
@@ -592,6 +611,104 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, msg=result.stderr or result.stdout)
|
||||
|
||||
def _run_operation_start_behavior_check(self, app_js: str) -> None:
|
||||
functions = "\n\n".join(
|
||||
[
|
||||
self._extract_js_function(app_js, "paneState"),
|
||||
self._extract_js_function(app_js, "otherPane"),
|
||||
self._extract_js_function(app_js, "defaultDestination"),
|
||||
self._extract_async_js_function(app_js, "startCopySelected"),
|
||||
self._extract_async_js_function(app_js, "executeMoveSelection"),
|
||||
]
|
||||
)
|
||||
script = textwrap.dedent(
|
||||
f"""
|
||||
const assert = (condition, message) => {{
|
||||
if (!condition) {{
|
||||
throw new Error(message);
|
||||
}}
|
||||
}};
|
||||
|
||||
let state = {{
|
||||
activePane: "left",
|
||||
panes: {{
|
||||
left: {{
|
||||
currentPath: "storage1/source",
|
||||
selectedItems: [
|
||||
{{ path: "storage1/source/a.txt", kind: "file", name: "a.txt" }},
|
||||
{{ path: "storage1/source/b.txt", kind: "file", name: "b.txt" }},
|
||||
],
|
||||
}},
|
||||
right: {{
|
||||
currentPath: "storage1/dest",
|
||||
selectedItems: [],
|
||||
}},
|
||||
}},
|
||||
selectedTaskId: null,
|
||||
}};
|
||||
|
||||
const apiCalls = [];
|
||||
const statusMessages = [];
|
||||
const errorCalls = [];
|
||||
const refreshCalls = [];
|
||||
const loadCalls = [];
|
||||
const clearedSelection = [];
|
||||
|
||||
async function apiRequest(method, url, body) {{
|
||||
apiCalls.push({{ method, url, body }});
|
||||
return {{ task_id: "task-123", status: "queued" }};
|
||||
}}
|
||||
function setError() {{}}
|
||||
function setActionError(action, err) {{ errorCalls.push({{ action, message: err.message }}); }}
|
||||
function setStatus(message) {{ statusMessages.push(message); }}
|
||||
async function refreshTasksSnapshot() {{ refreshCalls.push(true); }}
|
||||
async function loadBrowsePane(pane) {{ loadCalls.push(pane); }}
|
||||
function setSelectedItem(pane, value) {{ clearedSelection.push({{ pane, value }}); }}
|
||||
|
||||
{functions}
|
||||
|
||||
(async () => {{
|
||||
await startCopySelected();
|
||||
assert(apiCalls.length === 1, "Multi-select copy should issue one request");
|
||||
assert(apiCalls[0].url === "/api/files/copy", "Copy should use copy endpoint");
|
||||
assert(Array.isArray(apiCalls[0].body.sources), "Copy should send batch sources");
|
||||
assert(apiCalls[0].body.sources.length === 2, "Copy batch should include all selected items");
|
||||
assert(apiCalls[0].body.destination_base === "storage1/dest", "Copy batch should target destination base");
|
||||
assert(state.selectedTaskId === "task-123", "Copy should store the created task id");
|
||||
assert(refreshCalls.length === 1, "Copy should refresh task snapshot once");
|
||||
assert(statusMessages.includes("Copy: operation started"), "Copy should report operation start");
|
||||
|
||||
apiCalls.length = 0;
|
||||
refreshCalls.length = 0;
|
||||
statusMessages.length = 0;
|
||||
state.selectedTaskId = null;
|
||||
|
||||
await executeMoveSelection("storage1/dest");
|
||||
assert(apiCalls.length === 1, "Multi-select move should issue one request");
|
||||
assert(apiCalls[0].url === "/api/files/move", "Move should use move endpoint");
|
||||
assert(Array.isArray(apiCalls[0].body.sources), "Move should send batch sources");
|
||||
assert(apiCalls[0].body.sources.length === 2, "Move batch should include all selected items");
|
||||
assert(apiCalls[0].body.destination_base === "storage1/dest", "Move batch should target destination base");
|
||||
assert(state.selectedTaskId === "task-123", "Move should store the created task id");
|
||||
assert(refreshCalls.length === 1, "Move should refresh task snapshot once");
|
||||
assert(statusMessages.includes("Move: operation started"), "Move should report operation start");
|
||||
assert(clearedSelection.length === 1 && clearedSelection[0].pane === "left", "Move batch should clear source selection once");
|
||||
assert(errorCalls.length === 0, "Batch operation start should not emit action errors");
|
||||
}})().catch((error) => {{
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}});
|
||||
"""
|
||||
)
|
||||
result = subprocess.run(
|
||||
["node", "-e", script],
|
||||
cwd="/workspace/webmanager-mvp",
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, msg=result.stderr or result.stdout)
|
||||
|
||||
def test_ui_mount_and_index_contains_expected_panels(self) -> None:
|
||||
mount = self._ui_mount()
|
||||
self.assertIsInstance(mount.app, StaticFiles)
|
||||
@@ -1014,6 +1131,10 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
||||
self.assertIn('startCopySelected();', app_js)
|
||||
self.assertIn('openF6Flow();', app_js)
|
||||
self.assertIn('deleteSelected();', app_js)
|
||||
self.assertIn('sources: selectedItems.map((item) => item.path),', app_js)
|
||||
self.assertIn('destination_base: baseDestination,', app_js)
|
||||
self.assertIn('setStatus("Copy: operation started");', app_js)
|
||||
self.assertIn('setStatus("Move: operation started");', app_js)
|
||||
self.assertIn('const confirmed = await openConfirmModal({', app_js)
|
||||
self.assertIn('title: selectedItems.length === 1 ? "Delete item?" : "Delete selected items?"', app_js)
|
||||
self.assertIn('title: "Discard unsaved changes?"', app_js)
|
||||
@@ -1108,6 +1229,7 @@ class UiSmokeGoldenTest(unittest.TestCase):
|
||||
self.assertIn('input.setAttribute("webkitdirectory", "")', app_js)
|
||||
self.assertIn('await apiRequest("POST", "/api/files/mkdir", {', app_js)
|
||||
self.assertIn('await uploadFileRequest(targetPath, entry.file, overwrite);', app_js)
|
||||
self._run_operation_start_behavior_check(app_js)
|
||||
self.assertIn('Folder upload: preparing', app_js)
|
||||
self.assertIn('Folder upload: ${uploadState.successfulCount} uploaded, ${uploadState.skippedCount} skipped', app_js)
|
||||
self.assertIn('async function handleUploadSelection(event)', app_js)
|
||||
|
||||
Reference in New Issue
Block a user