fix local office resource editing

- add local-folder OnlyOffice sign/callback writeback and edit-tab handling

- align main resource tabs, attachment edit menu, slash isolation, and filetree context behavior

- record Sidex/Hermes gap reviews and Reasonix task checklists
This commit is contained in:
lix-2026
2026-05-21 05:40:06 +08:00
parent a29d9868f6
commit eba1010191
50 changed files with 4309 additions and 283 deletions
+77 -4
View File
@@ -191,6 +191,40 @@ async function main() {
);
console.log(` fileUrl uses Rust-accessible URL: ${fileUrlParam}`);
// ======== Test 1b: existing office tab refreshes from view to edit ========
console.log("Test 1b: existing docx tab refreshes from view mode to edit mode");
await page.evaluate(({ asset, documentId }) => {
window.dispatchEvent(new CustomEvent("tree.asset.open", {
detail: {
assetId: asset.id,
documentId,
title: asset.file_name || "report.docx",
assetType: asset.asset_type || "attachment",
openTarget: "edit-mode",
},
}));
}, { asset: officeAssets.docx, documentId });
await page.waitForFunction(() => {
const panel = document.querySelector('.mnote-resource-tab-panel:not([hidden])');
const iframe = panel?.querySelector('iframe.mnote-resource-tab-frame');
if (!(iframe instanceof HTMLIFrameElement)) return false;
const url = new URL(iframe.getAttribute("src") || iframe.src, window.location.origin);
return url.searchParams.get("mode") === "edit";
}, null, { timeout: UI_TIMEOUT_MS });
const refreshedTabInfo = await page.evaluate(() => {
const tabs = Array.from(document.querySelectorAll('.mnote-main-tab[data-mnote-tab-kind="office"]'));
const panel = document.querySelector('.mnote-resource-tab-panel:not([hidden])');
const iframe = panel?.querySelector('iframe.mnote-resource-tab-frame');
return {
officeTabCount: tabs.length,
iframeSrc: iframe?.getAttribute("src") || "",
};
});
const refreshedUrl = new URL(refreshedTabInfo.iframeSrc, BASE_URL);
console.log(` Refreshed active tab URL: ${refreshedUrl.toString()}`);
assert.equal(refreshedUrl.searchParams.get("mode"), "edit", "existing Office tab should refresh to mode=edit");
assert.equal(refreshedTabInfo.officeTabCount, 1, "edit-mode should refresh the existing docx tab instead of creating another tab");
// Close the docx tab
await page.evaluate(() => {
const officeTab = document.querySelector('.mnote-main-tab.is-active[data-mnote-tab-kind="office"]');
@@ -269,8 +303,8 @@ async function main() {
});
await page.waitForTimeout(300);
// ======== Test 4: new-window opens /onlyoffice with edit mode ========
console.log("Test 4: new-window opens /onlyoffice with edit mode");
// ======== Test 4: new-window defaults to view mode ========
console.log("Test 4: new-window opens /onlyoffice with view mode");
const [officePopup] = await Promise.all([
page.waitForEvent("popup", { timeout: UI_TIMEOUT_MS }).catch(() => null),
page.evaluate(({ asset, documentId }) => {
@@ -292,9 +326,9 @@ async function main() {
console.log(` New-window URL: ${popupUrl.toString()}`);
assert.equal(popupUrl.pathname, "/onlyoffice", `new-window pathname should be /onlyoffice: ${popupUrl.pathname}`);
// new-window should use edit mode
// new-window 是浏览器目标,不代表编辑权限;默认仍应只读
const popupMode = popupUrl.searchParams.get("mode") || "";
assert.equal(popupMode, "edit", `new-window mode should be 'edit', got '${popupMode}'`);
assert.equal(popupMode, "view", `new-window mode should be 'view', got '${popupMode}'`);
// Check assetId is present
const popupAssetId = popupUrl.searchParams.get("assetId") || "";
@@ -305,6 +339,45 @@ async function main() {
console.warn(" No popup triggered — this may mean the run is headless and popup was blocked. Acceptable in CI.");
}
// ======== Test 4b: explicit edit-mode opens /onlyoffice with edit mode ========
console.log("Test 4b: explicit edit-mode opens /onlyoffice with edit mode");
const [editPopup] = await Promise.all([
page.waitForEvent("popup", { timeout: UI_TIMEOUT_MS }).catch(() => null),
page.evaluate(({ asset, documentId }) => {
window.dispatchEvent(new CustomEvent("tree.asset.open", {
detail: {
assetId: asset.id,
documentId,
title: asset.file_name || "report.docx",
assetType: asset.asset_type || "attachment",
openTarget: "edit-mode",
},
}));
}, { asset: officeAssets.docx, documentId }),
]);
if (editPopup) {
await editPopup.waitForLoadState("domcontentloaded", { timeout: UI_TIMEOUT_MS }).catch(() => undefined);
const editUrl = new URL(editPopup.url());
console.log(` Edit-mode URL: ${editUrl.toString()}`);
assert.equal(editUrl.pathname, "/onlyoffice", `edit-mode pathname should be /onlyoffice: ${editUrl.pathname}`);
const editMode = editUrl.searchParams.get("mode") || "";
assert.equal(editMode, "edit", `edit-mode target should use mode=edit, got '${editMode}'`);
await editPopup.close().catch(() => undefined);
} else {
const editTabInfo = await page.evaluate(() => {
const panel = document.querySelector('.mnote-resource-tab-panel:not([hidden])');
const iframe = panel?.querySelector('iframe.mnote-resource-tab-frame');
return {
iframeSrc: iframe?.getAttribute("src") || "",
};
});
const editUrl = new URL(editTabInfo.iframeSrc, BASE_URL);
const editMode = editUrl.searchParams.get("mode") || "";
console.log(` Edit-mode active tab URL: ${editUrl.toString()}`);
assert.equal(editMode, "edit", `edit-mode active tab target should use mode=edit, got '${editMode}'`);
}
// ======== Test 5: OnlyOffice /onlyoffice page self-test (config URL/document URL/callback URL) ========
console.log("Test 5: /onlyoffice page structure validation");
const onlyofficeUrl = new URL("/onlyoffice", BASE_URL);