Files
mnote/scripts/task450-admin-access-policy-ui-smoke.js
T

174 lines
6.6 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { chromium } = require("playwright");
const BASE_URL = process.env.MNOTE_WEB_SMOKE_BASE_URL || "http://127.0.0.1:3000";
const UI_TIMEOUT_MS = Number(process.env.UI_TIMEOUT_MS || 10_000);
function resolveChromiumExecutablePath() {
const explicit = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || "";
if (explicit && fs.existsSync(explicit)) return explicit;
return [
"/usr/bin/google-chrome-stable",
"/usr/bin/google-chrome",
"/snap/bin/chromium",
"/usr/bin/chromium",
"/usr/bin/chromium-browser",
].find((candidate) => fs.existsSync(candidate)) || "";
}
function fileUrl(filePath) {
return `file://${filePath.split(path.sep).map((part, index) => (
index === 0 ? "" : encodeURIComponent(part)
)).join("/")}`;
}
async function waitForText(page, selector, expected) {
await page.waitForFunction(
({ selector: targetSelector, expectedText }) => {
const node = document.querySelector(targetSelector);
return Boolean(node && node.textContent && node.textContent.includes(expectedText));
},
{ selector, expectedText: expected },
{ timeout: UI_TIMEOUT_MS },
);
return page.locator(selector).innerText({ timeout: UI_TIMEOUT_MS });
}
async function main() {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "mnote-admin-access-policy-"));
fs.mkdirSync(path.join(root, ".mnote"), { recursive: true });
fs.writeFileSync(path.join(root, "README.md"), "# Admin Smoke\n", "utf8");
const executablePath = resolveChromiumExecutablePath();
const browser = await chromium.launch({
headless: true,
...(executablePath ? { executablePath } : {}),
});
const adminContext = await browser.newContext({
extraHTTPHeaders: {
"x-mnote-actor-id": "admin_smoke",
"x-mnote-actor-type": "admin",
},
});
const adminPage = await adminContext.newPage();
const readerContext = await browser.newContext({
extraHTTPHeaders: {
"x-mnote-actor-id": "reader_smoke",
"x-mnote-actor-type": "user",
},
});
const readerPage = await readerContext.newPage();
const grantId = `grant_${Date.now()}`;
const rootUri = fileUrl(root);
try {
await adminPage.goto(`${BASE_URL}/admin/access-policy`, {
waitUntil: "domcontentloaded",
timeout: UI_TIMEOUT_MS,
});
await adminPage.locator('[data-testid="mnote-admin-access-policy-page"]').waitFor({
state: "visible",
timeout: UI_TIMEOUT_MS,
});
assert(
await adminPage.locator('[data-testid="mnote-admin-policy-path"]').innerText(),
"管理页应显示策略路径",
);
await adminPage.locator('[data-testid="mnote-admin-root-uri"]').fill(rootUri);
await adminPage.locator('[data-testid="mnote-admin-validate-root-submit"]').click({
timeout: UI_TIMEOUT_MS,
});
const validateResult = await waitForText(
adminPage,
'[data-testid="mnote-admin-validate-result"]',
root,
);
assert(validateResult.includes(root), "验证目录结果应包含 canonical root");
await adminPage.locator('[data-testid="mnote-admin-grant-id"]').fill(grantId);
await adminPage.locator('[data-testid="mnote-admin-grant-user-id"]').fill("reader_smoke");
await adminPage.locator('[data-testid="mnote-admin-grant-root-uri"]').fill(rootUri);
await adminPage.locator('[data-testid="mnote-admin-grant-permission"]').selectOption("read");
await adminPage.locator('[data-testid="mnote-admin-grant-recursive"]').check();
await adminPage.locator('[data-testid="mnote-admin-grant-capabilities"]').fill("ai,share");
await adminPage.locator('[data-testid="mnote-admin-create-grant-submit"]').click({
timeout: UI_TIMEOUT_MS,
});
const createResult = await waitForText(
adminPage,
'[data-testid="mnote-admin-create-result"]',
grantId,
);
assert(createResult.includes(grantId), "创建结果应返回 grantId");
await readerPage.goto(BASE_URL, { waitUntil: "domcontentloaded", timeout: UI_TIMEOUT_MS });
const openResponse = await readerPage.evaluate(async ({ rootUriValue }) => {
const url = new URL("/api/local-folder/files/open", window.location.origin);
url.searchParams.set("rootUri", rootUriValue);
url.searchParams.set("path", "README.md");
const response = await fetch(url.toString(), { credentials: "include" });
return { status: response.status, text: await response.text() };
}, { rootUriValue: rootUri });
assert.equal(openResponse.status, 200, "read grant 应可打开本地文件");
assert(openResponse.text.includes("Admin Smoke"), "打开的文件内容应正确");
const writeResponse = await readerPage.evaluate(async ({ rootUriValue }) => {
const response = await fetch("/api/page-body/write", {
method: "POST",
headers: { "content-type": "application/json" },
credentials: "include",
body: JSON.stringify({
documentId: "local-md:README.md",
workspaceId: "local-ws-admin-smoke",
sourceKind: "local_folder",
rootUri: rootUriValue,
contentFormat: "editorBlocks",
content: [],
}),
});
return { status: response.status, text: await response.text() };
}, { rootUriValue: rootUri });
assert.equal(writeResponse.status, 403, "read grant 不应允许写入");
await adminPage.locator('[data-testid="mnote-admin-delete-grant-id"]').fill(grantId);
await adminPage.locator('[data-testid="mnote-admin-delete-grant-submit"]').click({
timeout: UI_TIMEOUT_MS,
});
const deleteResult = await waitForText(
adminPage,
'[data-testid="mnote-admin-delete-result"]',
grantId,
);
assert(deleteResult.includes(grantId), "删除结果应返回 grantId");
await adminPage.waitForFunction(
({ selector, removedGrantId }) => {
const node = document.querySelector(selector);
return Boolean(node && node.textContent && !node.textContent.includes(removedGrantId));
},
{ selector: '[data-testid="mnote-admin-policy-json"]', removedGrantId: grantId },
{ timeout: UI_TIMEOUT_MS },
);
const policyJson = await adminPage.locator('[data-testid="mnote-admin-policy-json"]').innerText();
assert(!policyJson.includes(grantId), "删除后策略面板不应再包含已删授权");
} finally {
await adminContext.close().catch(() => {});
await readerContext.close().catch(() => {});
await browser.close().catch(() => {});
fs.rmSync(root, { recursive: true, force: true });
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});