2026-06-01 09:29:12 +08:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
"use strict";
|
|
|
|
|
|
2026-07-28 17:04:27 +08:00
|
|
|
const { loginViaAuthForm } = require('./lib/browser-auth-login');
|
2026-06-01 09:29:12 +08:00
|
|
|
const assert = require("node:assert/strict");
|
|
|
|
|
const fs = require("node:fs");
|
|
|
|
|
const os = require("node:os");
|
|
|
|
|
const path = require("node:path");
|
|
|
|
|
const { chromium } = require("playwright");
|
|
|
|
|
|
|
|
|
|
const BASE_URL = (process.env.MNOTE_UI_BASE_URL || process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000").replace(/\/+$/, "");
|
|
|
|
|
const UI_TIMEOUT_MS = Number(process.env.MNOTE_SMOKE_UI_TIMEOUT_MS || 30_000);
|
|
|
|
|
const PROBE_DOCX_PATH = process.env.MNOTE_ONLYOFFICE_PROBE_DOCX
|
|
|
|
|
|| "/tmp/mnote-onlyoffice-manual/mnote-onlyoffice-upload-test.docx";
|
|
|
|
|
const OUT_DIR = path.join(process.cwd(), "tmp", "task524-workspace-object-identity-matrix-smoke");
|
|
|
|
|
const RESULT_PATH = path.join(OUT_DIR, "result.json");
|
|
|
|
|
const CHROMIUM_EXECUTABLE_PATH = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH
|
|
|
|
|
|| ["/usr/bin/google-chrome-stable", "/usr/bin/google-chrome", "/snap/bin/chromium", "/usr/bin/chromium"]
|
|
|
|
|
.find((candidate) => fs.existsSync(candidate));
|
|
|
|
|
|
|
|
|
|
function fileUrl(localPath) {
|
|
|
|
|
return `file://${localPath}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function localMdDocumentId(relativePath) {
|
|
|
|
|
return `local-md:${relativePath.replaceAll("/", "~2F")}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function documentUrl(root, relativePath) {
|
|
|
|
|
const url = new URL(`${BASE_URL}/documents/${encodeURIComponent(localMdDocumentId(relativePath))}`);
|
|
|
|
|
url.searchParams.set("sourceKind", "local_folder");
|
|
|
|
|
url.searchParams.set("rootUri", fileUrl(root));
|
|
|
|
|
url.searchParams.set("treeView", "filetree");
|
|
|
|
|
return url.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function writeWorkspaceManifest(root, ownerId, workspaceId) {
|
|
|
|
|
fs.mkdirSync(path.join(root, ".mnote"), { recursive: true });
|
|
|
|
|
fs.writeFileSync(
|
|
|
|
|
path.join(root, ".mnote", "workspace.json"),
|
|
|
|
|
`${JSON.stringify({
|
|
|
|
|
workspaceId,
|
|
|
|
|
ownerId,
|
|
|
|
|
createdAt: new Date().toISOString(),
|
|
|
|
|
capabilities: ["local_files", "markdown_edit", "asset_upload"],
|
|
|
|
|
}, null, 2)}\n`,
|
|
|
|
|
"utf8",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function quickLogin(page) {
|
2026-07-28 17:04:27 +08:00
|
|
|
// 7-76 P0: 标准表单登录(无测试快速登录按钮)
|
|
|
|
|
const base =
|
|
|
|
|
(typeof BASE_URL !== "undefined" && BASE_URL) ||
|
|
|
|
|
(typeof baseUrl !== "undefined" && baseUrl) ||
|
|
|
|
|
process.env.MNOTE_UI_BASE_URL ||
|
|
|
|
|
"http://127.0.0.1:3000";
|
|
|
|
|
const timeout =
|
|
|
|
|
(typeof UI_TIMEOUT_MS !== "undefined" && UI_TIMEOUT_MS) ||
|
|
|
|
|
(typeof TIMEOUT !== "undefined" && TIMEOUT) ||
|
|
|
|
|
30_000;
|
|
|
|
|
if (!String(page.url() || "").includes("/auth")) {
|
|
|
|
|
await page.goto(String(base).replace(/\/+$/, "") + "/auth", {
|
|
|
|
|
waitUntil: "commit",
|
|
|
|
|
timeout,
|
|
|
|
|
});
|
2026-06-01 09:29:12 +08:00
|
|
|
}
|
2026-07-28 17:04:27 +08:00
|
|
|
await loginViaAuthForm(page, {
|
|
|
|
|
baseUrl: base,
|
|
|
|
|
timeoutMs: timeout,
|
|
|
|
|
gotoAuth: false,
|
|
|
|
|
});
|
|
|
|
|
await page
|
|
|
|
|
.waitForURL((url) => !String(url).includes("/auth"), { timeout })
|
|
|
|
|
.catch(() => {});
|
2026-06-01 09:29:12 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function filetreeRowState(page, relativePath) {
|
|
|
|
|
return await page.evaluate((targetRelativePath) => {
|
|
|
|
|
const row = Array.from(document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]'))
|
|
|
|
|
.find((candidate) => candidate instanceof HTMLElement && candidate.getAttribute("data-local-relative-path") === targetRelativePath);
|
|
|
|
|
if (!(row instanceof HTMLElement)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const params = new URL(window.location.href).searchParams;
|
|
|
|
|
const workspacePath = window.__mnoteFileTreeRuntime?.readWorkspacePathFromRow?.(row, {
|
|
|
|
|
currentSourceKind: () => params.get("sourceKind") || document.body.dataset.mnoteSourceKind || "local_folder",
|
|
|
|
|
currentRootUri: () => params.get("rootUri") || document.body.dataset.mnoteRootUri || "",
|
|
|
|
|
resolveWorkspaceId: () => document.body.dataset.workspaceId || "",
|
|
|
|
|
rowTitle: (candidate) => candidate.textContent || "",
|
|
|
|
|
}) || null;
|
|
|
|
|
const objectIdentityRaw = row.getAttribute("data-object-identity") || "";
|
|
|
|
|
let objectIdentity = null;
|
|
|
|
|
try {
|
|
|
|
|
objectIdentity = objectIdentityRaw ? JSON.parse(objectIdentityRaw) : null;
|
|
|
|
|
} catch {
|
|
|
|
|
objectIdentity = objectIdentityRaw;
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
rowId: row.getAttribute("data-row-id") || "",
|
|
|
|
|
rowKind: row.getAttribute("data-row-kind") || "",
|
|
|
|
|
documentId: row.getAttribute("data-document-id") || "",
|
|
|
|
|
assetId: row.getAttribute("data-asset-id") || "",
|
|
|
|
|
relativePath: row.getAttribute("data-local-relative-path") || "",
|
|
|
|
|
objectIdentity,
|
|
|
|
|
workspacePath,
|
|
|
|
|
expanded: row.getAttribute("aria-expanded") || "",
|
|
|
|
|
};
|
|
|
|
|
}, relativePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function clickFiletreeOpen(page, relativePath) {
|
|
|
|
|
const row = page.locator(`#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="${relativePath.replace(/"/g, '\\"')}"]`).first();
|
|
|
|
|
await row.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
|
|
|
|
await row.locator('[data-rust-action="open"]').click({ timeout: UI_TIMEOUT_MS });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function waitFiletreeRow(page, relativePath) {
|
|
|
|
|
const row = page.locator(`#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="${relativePath.replace(/"/g, '\\"')}"]`).first();
|
|
|
|
|
await row.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function expandFiletreeFolder(page, relativePath) {
|
|
|
|
|
const row = page.locator(`#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="${relativePath.replace(/"/g, '\\"')}"]`).first();
|
|
|
|
|
await row.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS });
|
|
|
|
|
const expanded = await row.getAttribute("aria-expanded");
|
|
|
|
|
if (expanded === "true") return;
|
|
|
|
|
await row.locator('[data-rust-action="open"]').click({ timeout: UI_TIMEOUT_MS });
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
|
(targetRelativePath) => {
|
|
|
|
|
const candidate = Array.from(document.querySelectorAll('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"]'))
|
|
|
|
|
.find((row) => row instanceof HTMLElement && row.getAttribute("data-local-relative-path") === targetRelativePath);
|
|
|
|
|
return candidate && candidate.getAttribute("aria-expanded") === "true";
|
|
|
|
|
},
|
|
|
|
|
relativePath,
|
|
|
|
|
{ timeout: UI_TIMEOUT_MS },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function activeSnapshotEntry(page, expectedRelativePath, expectedKind) {
|
|
|
|
|
return await page.waitForFunction(
|
|
|
|
|
({ expectedRelativePath, expectedKind }) => {
|
|
|
|
|
const snapshot = window.__mnoteOpenEditorsSnapshot || null;
|
|
|
|
|
const liveSnapshot = window.__mnoteDocumentPaneRuntime?.getOpenEditorsSnapshot?.() || snapshot;
|
|
|
|
|
const resources = Array.isArray(liveSnapshot?.resourceEditors) ? liveSnapshot.resourceEditors : [];
|
|
|
|
|
const entries = Array.isArray(liveSnapshot?.editors) ? liveSnapshot.editors.concat(resources) : resources;
|
|
|
|
|
const activeObjectIdentity = String(liveSnapshot?.activeObjectIdentity || "");
|
|
|
|
|
const active = entries.find((entry) =>
|
|
|
|
|
entry
|
|
|
|
|
&& (entry.active === true || entry.objectIdentity === activeObjectIdentity)
|
|
|
|
|
&& (!expectedRelativePath || entry.workspacePath?.relativePath === expectedRelativePath || entry.path === expectedRelativePath)
|
|
|
|
|
&& (!expectedKind || entry.kind === expectedKind || entry.editorKind === expectedKind || entry.workspacePath?.resourceKind === expectedKind)
|
|
|
|
|
);
|
|
|
|
|
return active || null;
|
|
|
|
|
},
|
|
|
|
|
{ expectedRelativePath, expectedKind },
|
|
|
|
|
{ timeout: UI_TIMEOUT_MS },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function assertWorkspacePathMatchesRow(label, rowState, snapshotEntry, expected) {
|
|
|
|
|
assert(rowState, `${label}: 缺少 FileTree row state`);
|
|
|
|
|
assert(rowState.workspacePath, `${label}: FileTree row 缺少 workspacePath: ${JSON.stringify(rowState)}`);
|
|
|
|
|
assert(snapshotEntry, `${label}: 缺少 OpenEditorsSnapshot entry`);
|
|
|
|
|
assert.equal(rowState.workspacePath.schema, "mnote.workspace_path.v1", `${label}: row workspacePath schema`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.schema, "mnote.workspace_path.v1", `${label}: snapshot workspacePath schema`);
|
|
|
|
|
assert.equal(rowState.workspacePath.relativePath, expected.relativePath, `${label}: row relativePath`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.relativePath, expected.relativePath, `${label}: snapshot relativePath`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.sourceKind, rowState.workspacePath.sourceKind, `${label}: sourceKind 应一致`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.rootUri, rowState.workspacePath.rootUri, `${label}: rootUri 应一致`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.documentId, rowState.workspacePath.documentId, `${label}: documentId 应一致`);
|
|
|
|
|
if (expected.assetId) {
|
|
|
|
|
assert.equal(rowState.workspacePath.assetId, expected.assetId, `${label}: row assetId`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.assetId, expected.assetId, `${label}: snapshot assetId`);
|
|
|
|
|
}
|
|
|
|
|
if (expected.resourceKind) {
|
|
|
|
|
assert.equal(rowState.workspacePath.resourceKind, expected.resourceKind, `${label}: row resourceKind`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.resourceKind, expected.resourceKind, `${label}: resourceKind`);
|
|
|
|
|
}
|
|
|
|
|
if (expected.objectKind) {
|
|
|
|
|
assert.equal(rowState.workspacePath.objectIdentity?.objectKind, expected.objectKind, `${label}: row objectKind`);
|
|
|
|
|
assert.equal(snapshotEntry.workspacePath?.objectIdentity?.objectKind, expected.objectKind, `${label}: snapshot objectKind`);
|
|
|
|
|
}
|
|
|
|
|
if (rowState.workspacePath.objectIdentity && typeof rowState.workspacePath.objectIdentity === "object"
|
|
|
|
|
&& snapshotEntry.workspacePath?.objectIdentity && typeof snapshotEntry.workspacePath.objectIdentity === "object") {
|
|
|
|
|
assert.deepStrictEqual(
|
|
|
|
|
snapshotEntry.workspacePath.objectIdentity,
|
|
|
|
|
rowState.workspacePath.objectIdentity,
|
|
|
|
|
`${label}: snapshot objectIdentity 应与 row objectIdentity 对齐`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function main() {
|
|
|
|
|
fs.mkdirSync(OUT_DIR, { recursive: true });
|
|
|
|
|
const actorId = "mnote-e2e";
|
|
|
|
|
const workspaceId = `local-ws:${actorId}:task524`;
|
|
|
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-task524-identity-"));
|
|
|
|
|
const rootUri = fileUrl(root);
|
|
|
|
|
fs.mkdirSync(path.join(root, "Page"), { recursive: true });
|
|
|
|
|
fs.mkdirSync(path.join(root, "docs"), { recursive: true });
|
|
|
|
|
writeWorkspaceManifest(root, actorId, workspaceId);
|
|
|
|
|
fs.writeFileSync(path.join(root, "Page.md"), "# Page\n\nTask524 page\n", "utf8");
|
|
|
|
|
fs.writeFileSync(path.join(root, "docs", "Plan.md"), "# Plan\n\nTask524 plan\n", "utf8");
|
|
|
|
|
fs.writeFileSync(path.join(root, "Page", "notes.txt"), "Task524 text resource\n", "utf8");
|
|
|
|
|
fs.writeFileSync(path.join(root, "Page", "map.mindmap.json"), JSON.stringify({
|
|
|
|
|
root: { data: { text: "Task524 Mindmap" }, children: [] },
|
|
|
|
|
}, null, 2), "utf8");
|
|
|
|
|
if (fs.existsSync(PROBE_DOCX_PATH)) {
|
|
|
|
|
fs.copyFileSync(PROBE_DOCX_PATH, path.join(root, "Page", "office.docx"));
|
|
|
|
|
} else {
|
|
|
|
|
fs.writeFileSync(path.join(root, "Page", "office.docx"), "task524 office probe\n", "utf8");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const browser = await chromium.launch({
|
|
|
|
|
headless: process.env.HEADFUL !== "1",
|
|
|
|
|
...(CHROMIUM_EXECUTABLE_PATH ? { executablePath: CHROMIUM_EXECUTABLE_PATH } : {}),
|
|
|
|
|
});
|
|
|
|
|
const context = await browser.newContext({
|
|
|
|
|
viewport: { width: 1440, height: 960 },
|
|
|
|
|
extraHTTPHeaders: {
|
|
|
|
|
"x-mnote-actor-id": actorId,
|
|
|
|
|
"x-mnote-actor-type": "user",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const page = await context.newPage();
|
|
|
|
|
const result = {
|
|
|
|
|
ok: false,
|
|
|
|
|
task: "task524-workspace-object-identity-matrix-smoke",
|
|
|
|
|
baseUrl: BASE_URL,
|
|
|
|
|
root,
|
|
|
|
|
rootUri,
|
|
|
|
|
checks: [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await quickLogin(page);
|
|
|
|
|
await page.goto(documentUrl(root, "Page.md"), { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
|
|
|
|
|
await page.locator(".document-pane[data-pane-role=\"primary\"] .editor-surface .ProseMirror").first().waitFor({
|
|
|
|
|
state: "visible",
|
|
|
|
|
timeout: UI_TIMEOUT_MS,
|
|
|
|
|
});
|
|
|
|
|
await page.waitForFunction(() => Boolean(window.__mnoteFileTreeRuntime?.readWorkspacePathFromRow), null, {
|
|
|
|
|
timeout: UI_TIMEOUT_MS,
|
|
|
|
|
});
|
|
|
|
|
await page.waitForFunction(() => Boolean(window.__mnoteOpenEditorsSnapshot), null, {
|
|
|
|
|
timeout: UI_TIMEOUT_MS,
|
|
|
|
|
});
|
|
|
|
|
await page.evaluate(() => {
|
|
|
|
|
window.__task524IdentityEvents = [];
|
|
|
|
|
window.addEventListener("tree.filetree.open", (event) => {
|
|
|
|
|
window.__task524IdentityEvents.push({ type: "tree.filetree.open", detail: event.detail || null });
|
|
|
|
|
});
|
|
|
|
|
window.addEventListener("tree.asset.open", (event) => {
|
|
|
|
|
window.__task524IdentityEvents.push({ type: "tree.asset.open", detail: event.detail || null });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const pageRow = await filetreeRowState(page, "Page.md");
|
|
|
|
|
const pageEntry = await page.evaluate(() => {
|
|
|
|
|
const snapshot = window.__mnoteDocumentPaneRuntime?.getOpenEditorsSnapshot?.() || window.__mnoteOpenEditorsSnapshot || null;
|
|
|
|
|
return snapshot?.groups?.primary?.editors?.find((entry) => entry.kind === "page" && entry.documentId === "local-md:Page.md") || null;
|
|
|
|
|
});
|
|
|
|
|
assertWorkspacePathMatchesRow("page", pageRow, pageEntry, {
|
|
|
|
|
relativePath: "Page.md",
|
|
|
|
|
resourceKind: "page",
|
|
|
|
|
objectKind: "page",
|
|
|
|
|
});
|
|
|
|
|
result.checks.push({ kind: "page", row: pageRow, snapshot: pageEntry });
|
|
|
|
|
|
|
|
|
|
const folderRow = await filetreeRowState(page, "docs");
|
|
|
|
|
assert(folderRow, "folder: 缺少 docs row");
|
|
|
|
|
assert.equal(folderRow.workspacePath?.schema, "mnote.workspace_path.v1", "folder row workspacePath schema");
|
|
|
|
|
assert.equal(folderRow.workspacePath?.relativePath, "docs", "folder row relativePath");
|
|
|
|
|
assert.equal(folderRow.workspacePath?.documentId, "local-dir:docs", "folder row documentId");
|
|
|
|
|
assert.equal(folderRow.workspacePath?.objectIdentity?.objectKind, "index", "folder row objectKind");
|
|
|
|
|
const beforeFolderUrl = page.url();
|
|
|
|
|
await clickFiletreeOpen(page, "docs");
|
|
|
|
|
await page.waitForFunction(() => {
|
|
|
|
|
const row = document.querySelector('#sidebar-file-tree-root .tree-row[data-shell-mode="filetree"][data-local-relative-path="docs"]');
|
|
|
|
|
return row && row.getAttribute("aria-expanded") === "true";
|
|
|
|
|
}, null, { timeout: UI_TIMEOUT_MS });
|
|
|
|
|
assert.equal(page.url(), beforeFolderUrl, "FileTree folder open 只应展开,不应改 URL");
|
|
|
|
|
result.checks.push({ kind: "folder", row: folderRow });
|
|
|
|
|
|
|
|
|
|
await expandFiletreeFolder(page, "Page");
|
|
|
|
|
|
|
|
|
|
const resources = [
|
|
|
|
|
{ label: "text", relativePath: "Page/notes.txt", kind: "text", resourceKind: "attachment", objectKind: "attachment" },
|
|
|
|
|
{ label: "mindmap", relativePath: "Page/map.mindmap.json", kind: "mindmap", resourceKind: "mindmap", objectKind: "mindmap" },
|
|
|
|
|
{ label: "office", relativePath: "Page/office.docx", kind: "office", resourceKind: "only_office", objectKind: "only_office" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
for (const resource of resources) {
|
|
|
|
|
await waitFiletreeRow(page, resource.relativePath);
|
|
|
|
|
const rowState = await filetreeRowState(page, resource.relativePath);
|
|
|
|
|
assert(rowState, `${resource.label}: 缺少 FileTree row`);
|
|
|
|
|
await clickFiletreeOpen(page, resource.relativePath);
|
|
|
|
|
const entryHandle = await activeSnapshotEntry(page, resource.relativePath, resource.kind);
|
|
|
|
|
const entry = await entryHandle.jsonValue();
|
|
|
|
|
assertWorkspacePathMatchesRow(resource.label, rowState, entry, {
|
|
|
|
|
relativePath: resource.relativePath,
|
|
|
|
|
assetId: rowState.workspacePath.assetId,
|
|
|
|
|
resourceKind: resource.resourceKind,
|
|
|
|
|
objectKind: resource.objectKind,
|
|
|
|
|
});
|
|
|
|
|
assert.equal(entry.kind, resource.kind, `${resource.label}: editor kind`);
|
|
|
|
|
const eventDetail = await page.evaluate((relativePath) => {
|
|
|
|
|
const events = Array.isArray(window.__task524IdentityEvents) ? window.__task524IdentityEvents : [];
|
|
|
|
|
return [...events].reverse().find((event) => event?.detail?.workspacePath?.relativePath === relativePath) || null;
|
|
|
|
|
}, resource.relativePath);
|
|
|
|
|
assert(eventDetail, `${resource.label}: 应捕获真实 tree.asset.open 事件`);
|
|
|
|
|
assert.equal(eventDetail.detail.workspacePath?.sourceKind, rowState.workspacePath.sourceKind, `${resource.label}: event sourceKind`);
|
|
|
|
|
assert.equal(eventDetail.detail.workspacePath?.rootUri, rowState.workspacePath.rootUri, `${resource.label}: event rootUri`);
|
|
|
|
|
assert.equal(eventDetail.detail.workspacePath?.documentId, rowState.workspacePath.documentId, `${resource.label}: event documentId`);
|
|
|
|
|
assert.equal(eventDetail.detail.workspacePath?.assetId, rowState.workspacePath.assetId, `${resource.label}: event assetId`);
|
|
|
|
|
assert.deepStrictEqual(eventDetail.detail.workspacePath?.objectIdentity, rowState.workspacePath.objectIdentity, `${resource.label}: event objectIdentity`);
|
|
|
|
|
const url = new URL(page.url());
|
|
|
|
|
const resourceTab = url.searchParams.get("resourceTab") || "";
|
|
|
|
|
assert(
|
|
|
|
|
resourceTab === `primary::${entry.objectIdentity}`,
|
|
|
|
|
`${resource.label}: URL resourceTab 应等于 active snapshot objectIdentity: ${JSON.stringify({ resourceTab, entry })}`,
|
|
|
|
|
);
|
|
|
|
|
const activeTabIdentity = await page.evaluate(() => {
|
|
|
|
|
const activeTab = document.querySelector(".mnote-main-tab.is-active");
|
|
|
|
|
return activeTab?.getAttribute("data-mnote-object-identity") || "";
|
|
|
|
|
});
|
|
|
|
|
if (activeTabIdentity) {
|
|
|
|
|
assert.equal(activeTabIdentity, entry.objectIdentity, `${resource.label}: active tab identity`);
|
|
|
|
|
}
|
|
|
|
|
result.checks.push({ kind: resource.label, row: rowState, snapshot: entry, resourceTab, activeTabIdentity });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.ok = true;
|
|
|
|
|
fs.writeFileSync(RESULT_PATH, `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
|
|
|
|
console.log(JSON.stringify(result, null, 2));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
result.error = error && error.stack ? error.stack : String(error);
|
|
|
|
|
fs.writeFileSync(RESULT_PATH, `${JSON.stringify(result, null, 2)}\n`, "utf8");
|
|
|
|
|
throw error;
|
|
|
|
|
} finally {
|
|
|
|
|
await context.close().catch(() => undefined);
|
|
|
|
|
await browser.close().catch(() => undefined);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main().catch((error) => {
|
|
|
|
|
console.error(error && error.stack ? error.stack : error);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
});
|