Files
govoplan-files/webui/scripts/test-managed-archive-structure.mjs
zemion ff84812f7f
Module Package Release / publish-packages (push) Successful in 12s
Release govoplan-files v0.1.26: speed archive workflows and unify file tools
2026-09-08 01:32:41 +02:00

61 lines
5.1 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import vm from "node:vm";
const read = (path) => readFileSync(new URL(path, import.meta.url), "utf8");
const page = read("../src/features/files/FilesPage.tsx");
const menu = read("../src/features/files/components/FileManagerComponents.tsx");
const api = read("../src/api/files.ts");
const translations = read("../src/i18n/generatedTranslations.ts");
assert.match(page, /selectedFiles\.length === 1 && selectedFolderPaths\.size === 0 && ARCHIVE_FILENAME_PATTERN/);
assert.match(page, /function openManagedArchive[\s\S]*?!canUpload \|\| !canDownload[\s\S]*?setManagedArchiveFile\(file\)/);
assert.match(page, /disabledReason=\{unpackBlocker\}/);
assert.match(menu, /onClick=\{onUnpackArchive\} disabled=\{!canUnpackArchive\}/);
assert.match(page, /file: File \| ManagedFile/);
assert.match(page, /previewManagedArchive\(settings, file\.id,[\s\S]*?source_version_id: file\.version_id/);
assert.match(page, /confirmManagedArchive\(settings, managedArchiveFile\.id,[\s\S]*?source_version_id: managedArchiveFile\.version_id/);
assert.match(page, /archivePreview && \(archiveFile \|\| managedArchiveFile\)/);
assert.match(page, /loadArchivePreview\(\(managedArchiveFile \|\| archiveFile\)!/);
assert.match(page, /if \(managedArchiveFile\) \{ setArchivePreview\(null\); setSelectedArchivePaths/);
assert.match(page, /onClose=\{\(\) => \{ if \(!busy\) closeDialog\(\); \}\}/);
assert.match(page, /<LoadingFrame loading=\{uploadActive\}[\s\S]*?indicator="none"[\s\S]*?progress=\{operationProgressValue\}/);
assert.match(page, /<div inert=\{uploadActive\}>/);
assert.match(page, /onArchiveProgress: \(progress: ArchiveOperationProgress\)/);
assert.match(page, /archiveProgress\?\.status === "complete" \? 100/);
assert.match(page, /archiveProgress.phase !== "finalizing"[\s\S]*?serverProgressRatio < 1/);
assert.match(menu, /closeDisabled=\{busy\}[\s\S]*?closeOnBackdrop=\{!busy\}/);
const feedback = page.slice(page.indexOf(" const selectedArchiveBytes ="), page.indexOf(" const connectorLocationLabel ="));
function operationFeedback(archiveProgress, uploadPhase = "unpacking", uploadProgress = null) {
const context = { archiveProgress, uploadPhase, uploadProgress, selectedArchivePaths: new Set(["one.txt", "empty.txt"]),
archivePreview: { entries: [{ path: "one.txt", kind: "file", size_bytes: 3 }, { path: "empty.txt", kind: "file", size_bytes: 0 }] },
formatBytes: (value) => `${value} B`, i18nMessage: (key, values) => ({ key, values }) };
vm.runInNewContext(`${feedback}\nglobalThis.result = { value: operationProgressValue, label: operationProgressLabel, phase: operationBusyLabel };`, context);
return context.result;
}
const measured = { phase: "extracting", status: "running", completed_files: 1, total_files: 2, completed_bytes: 3, total_bytes: 6 };
assert.equal(operationFeedback(measured).value, 50);
assert.equal(operationFeedback({ ...measured, phase: "finalizing", completed_files: 2, completed_bytes: 6 }).value, null, "finalization does not fabricate completion");
assert.equal(operationFeedback({ ...measured, completed_files: 2, completed_bytes: 6 }).value, null, "processed bytes alone do not mean commit success");
assert.equal(operationFeedback({ ...measured, phase: "complete", status: "complete" }).value, 100);
assert.equal(operationFeedback({ ...measured, total_bytes: 0, completed_bytes: 0 }).value, 50, "empty members use actual file counts");
assert.equal(operationFeedback(null).value, null, "no measured result remains indeterminate");
assert.equal(operationFeedback(null).label.values.total, 2, "selected totals remain available before polling responds");
assert.equal(operationFeedback(null).label.values.bytes, "3 B");
assert.equal(operationFeedback(null, "uploading", 24).value, 24);
assert.equal(operationFeedback(null, "uploading", 100).value, null, "completed transfer is not completed extraction");
assert.equal(page.split('event.dataTransfer.effectAllowed = "copyMove";').length - 1, 2);
assert.doesNotMatch(page, /effectAllowed = "i18n:/, "browser drag/drop enums must not be translated");
for (const name of ["previewManagedArchive", "confirmManagedArchive"]) {
const body = api.slice(api.indexOf(`export function ${name}`)).split("\n}\n", 1)[0];
assert.match(body, /encodeURIComponent\(fileId\)/);
assert.match(body, /body: JSON\.stringify\(/);
assert.doesNotMatch(body, /FormData|downloadFile|Blob|createObjectURL/);
}
assert.match(page, /function resetArchiveUploadState\(\) \{\s*void releaseArchivePreview\(settings, archiveFile\)/, "reset/cancel releases any temporary archive before forgetting it");
assert.match(page, /archiveFile && archiveFile !== file\) void releaseArchivePreview/, "replacing a selected archive releases its previous temporary upload");
for (const key of ["unpack", "preview", "select_one", "source", "expires", "extracting", "change_destination"]) {
assert.equal(translations.split(`"i18n:govoplan-files.managed_archive.${key}":`).length - 1, 2, `${key} must have EN and DE text`);
}
console.log("Managed archive actions reuse the preview dialog and send source-bound JSON without browser re-upload.");