feat: consolidate local-first mnote web runtime
This commit is contained in:
@@ -64,6 +64,7 @@ async function uploadLocalAsset(page, root, documentId, fileName, mimeType, byte
|
||||
const form = new FormData();
|
||||
form.append("rootUri", rootUri);
|
||||
form.append("documentId", documentId);
|
||||
form.append("uploadIntent", "editor.markdown.attach");
|
||||
form.append("kind", kind);
|
||||
form.append("file", new File([new Uint8Array(bytes)], fileName, { type: mimeType }));
|
||||
const response = await fetch("/api/local-folder/assets/upload", { method: "POST", body: form });
|
||||
@@ -169,6 +170,16 @@ async function main() {
|
||||
assert(!/\b(description|article|notes)\b/i.test(markdownTabState.resourceText), `Markdown tab 不应显示 Material Symbols 文本: ${JSON.stringify(markdownTabState)}`);
|
||||
assert.equal(markdownTabState.badgeKind, "code", `Markdown tab 应使用黑色 code 色块: ${JSON.stringify(markdownTabState)}`);
|
||||
assert(markdownTabState.badgeColor === "rgb(17, 17, 17)", `Markdown tab 色块应为黑色: ${JSON.stringify(markdownTabState)}`);
|
||||
const openEditorsSnapshot = await page.evaluate(() => window.__mnoteOpenEditorsSnapshot || null);
|
||||
assert.equal(openEditorsSnapshot?.schema, "mnote.open_editors_snapshot.v1", `应输出 open editors snapshot: ${JSON.stringify(openEditorsSnapshot)}`);
|
||||
assert(openEditorsSnapshot.groups?.primary, `snapshot 应包含 primary group: ${JSON.stringify(openEditorsSnapshot)}`);
|
||||
assert(openEditorsSnapshot.groups?.secondary, `snapshot 应包含 secondary group: ${JSON.stringify(openEditorsSnapshot)}`);
|
||||
assert(openEditorsSnapshot.activeObjectIdentity.includes("resource-note.md"), `全局 active 应优先 primary active resource,而不是 secondary page: ${JSON.stringify(openEditorsSnapshot)}`);
|
||||
assert(openEditorsSnapshot.groups.primary.activeObjectIdentity.includes("resource-note.md"), `primary group active 应指向 markdown resource: ${JSON.stringify(openEditorsSnapshot.groups.primary)}`);
|
||||
assert(openEditorsSnapshot.groups.primary.resourceEditors.some((entry) => entry.paneRole === "primary" && entry.kind === "markdown" && entry.preview === false && entry.pinned === false), `primary resource editor 应包含轻量 editor 字段: ${JSON.stringify(openEditorsSnapshot.groups.primary)}`);
|
||||
assert(openEditorsSnapshot.groups.primary.editors.some((entry) => entry.kind === "page" && entry.pinned === true), `primary group 应包含 pinned page editor: ${JSON.stringify(openEditorsSnapshot.groups.primary)}`);
|
||||
assert(openEditorsSnapshot.groups.secondary.editors.some((entry) => entry.kind === "page" && entry.paneRole === "secondary"), `secondary group 应包含 secondary page editor: ${JSON.stringify(openEditorsSnapshot.groups.secondary)}`);
|
||||
assert(openEditorsSnapshot.groups.secondary.editors.some((entry) => entry.kind === "page" && entry.documentId === localMdDocumentId(sideRelativePath) && entry.workspacePath?.relativePath === sideRelativePath), `secondary page editor 应保留自己的 documentId/workspacePath: ${JSON.stringify(openEditorsSnapshot.groups.secondary)}`);
|
||||
const editor = page.locator('.mnote-resource-tab-panel:not([hidden]) .editor-surface .ProseMirror[contenteditable="true"]').first();
|
||||
await editor.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
||||
await editor.click({ timeout: UI_TIMEOUT_MS });
|
||||
@@ -213,14 +224,24 @@ async function main() {
|
||||
assert(!/\b(description|article|notes)\b/i.test(officeTabState.resourceText), `Office tab 不应显示 Material Symbols 文本: ${JSON.stringify(officeTabState)}`);
|
||||
assert.equal(officeTabState.badgeKind, "word", `docx tab 应使用 word 色块: ${JSON.stringify(officeTabState)}`);
|
||||
assert(officeTabState.badgeColor === "rgb(79, 125, 243)", `docx tab 色块应与附件 word 蓝色一致: ${JSON.stringify(officeTabState)}`);
|
||||
const openEditorsSnapshotAfterOffice = await page.evaluate(() => window.__mnoteOpenEditorsSnapshot || null);
|
||||
const activeOfficeEditor = openEditorsSnapshotAfterOffice?.groups?.primary?.resourceEditors?.find((entry) => entry.active && entry.kind === "office");
|
||||
assert(activeOfficeEditor, `打开 Office 后 snapshot 应把 office resource 标为 active: ${JSON.stringify(openEditorsSnapshotAfterOffice?.groups?.primary)}`);
|
||||
assert.equal(activeOfficeEditor.documentId, documentId, `Office snapshot 应携带 documentId: ${JSON.stringify(activeOfficeEditor)}`);
|
||||
assert(activeOfficeEditor.workspaceId, `Office snapshot 应携带 workspaceId: ${JSON.stringify(activeOfficeEditor)}`);
|
||||
assert.equal(activeOfficeEditor.editorKind, "office", `Office snapshot 应携带 editorKind: ${JSON.stringify(activeOfficeEditor)}`);
|
||||
assert(activeOfficeEditor.lastActiveAt > 0, `Office passive resource 激活后 lastActiveAt 应大于 0: ${JSON.stringify(activeOfficeEditor)}`);
|
||||
const officeFrameSrc = await page.locator('.mnote-resource-tab-panel:not([hidden]) iframe.mnote-resource-tab-frame').first().getAttribute("src", {
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
assert(officeFrameSrc, "Office resource tab 应创建 iframe");
|
||||
const officeFrameUrl = new URL(officeFrameSrc, BASE_URL);
|
||||
assert(officeFrameUrl.pathname === "/onlyoffice", `Office resource tab iframe 应指向 /onlyoffice,实际为 ${officeFrameSrc}`);
|
||||
assert(officeFrameUrl.searchParams.get("assetId") === officeAsset.id, "Office iframe URL 应携带当前资源 assetId");
|
||||
assert(officeFrameUrl.searchParams.get("mode") === "view", "Office resource tab iframe 应使用 view 模式");
|
||||
assert(officeFrameUrl.pathname === "/office-preview", `Office resource tab iframe 应指向 /office-preview 轻量预览,实际为 ${officeFrameSrc}`);
|
||||
assert(officeFrameUrl.searchParams.get("assetId") === officeAsset.id, "Office preview URL 应携带当前资源 assetId");
|
||||
assert(officeFrameUrl.searchParams.get("documentId") === documentId, "Office preview URL 应携带当前 documentId");
|
||||
assert(officeFrameUrl.searchParams.get("fileName") === "resource-office.docx", "Office preview URL 应携带资源文件名");
|
||||
assert(officeFrameUrl.searchParams.get("fileType") === "docx", "Office preview URL 应携带 docx 类型");
|
||||
assert(officeFrameUrl.searchParams.get("fileUrl")?.includes("/api/local-folder/files/open"), "Office preview URL 应通过 local-folder file open 回源");
|
||||
|
||||
const [officePopup] = await Promise.all([
|
||||
page.waitForEvent("popup", { timeout: UI_TIMEOUT_MS }),
|
||||
@@ -271,6 +292,31 @@ async function main() {
|
||||
if (markdownActiveState.resourceRows.includes(asset.id)) {
|
||||
assert.equal(markdownActiveState.activeRowAssetId, asset.id, `已渲染对应 filetree row 时应标记 data-active: ${JSON.stringify(markdownActiveState)}`);
|
||||
}
|
||||
await page.reload({ waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
||||
await page.locator('.mnote-main-tab.is-active[data-mnote-tab-kind="markdown"]').waitFor({
|
||||
state: "visible",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
await page.locator('.mnote-resource-tab-panel:not([hidden]) .editor-surface .ProseMirror[contenteditable="true"]').first().waitFor({
|
||||
state: "visible",
|
||||
timeout: UI_TIMEOUT_MS,
|
||||
});
|
||||
const restoredMarkdownState = await page.evaluate(() => {
|
||||
const snapshot = window.__mnoteOpenEditorsSnapshot || null;
|
||||
const active = snapshot?.groups?.primary?.resourceEditors?.find((entry) => entry.active && entry.kind === "markdown");
|
||||
return {
|
||||
activeResource: new URL(window.location.href).searchParams.get("resourceTab") || "",
|
||||
activeObjectIdentity: active?.objectIdentity || "",
|
||||
path: active?.path || "",
|
||||
workspacePath: active?.workspacePath || null,
|
||||
editorText: document.querySelector('.mnote-resource-tab-panel:not([hidden]) .editor-surface .ProseMirror')?.textContent || "",
|
||||
};
|
||||
});
|
||||
assert(restoredMarkdownState.activeResource.includes("resource-note.md"), `刷新后应保留 canonical resourceTab URL 状态: ${JSON.stringify(restoredMarkdownState)}`);
|
||||
assert(restoredMarkdownState.activeObjectIdentity.includes("resource-note.md"), `刷新后应从 OpenEditorsSnapshot 恢复 active markdown resource: ${JSON.stringify(restoredMarkdownState)}`);
|
||||
assert.equal(restoredMarkdownState.path, "README/resource-note.md", `刷新后 active resource snapshot 应保留 path: ${JSON.stringify(restoredMarkdownState)}`);
|
||||
assert.equal(restoredMarkdownState.workspacePath?.relativePath, "README/resource-note.md", `刷新后 active resource 应带 workspacePath.relativePath: ${JSON.stringify(restoredMarkdownState)}`);
|
||||
assert(restoredMarkdownState.editorText.includes("新增资源正文"), `刷新后 resource editor 应读回资源正文: ${JSON.stringify(restoredMarkdownState)}`);
|
||||
|
||||
const mdCloseBtn = page.locator('.mnote-main-tab.is-active[data-mnote-tab-kind="markdown"] .mnote-main-tab-close');
|
||||
await mdCloseBtn.click({ timeout: UI_TIMEOUT_MS });
|
||||
|
||||
Reference in New Issue
Block a user