"use strict"; const { chromium } = require("playwright"); const { BASE_URL, UI_TIMEOUT_MS, assert, ensureAuthenticated } = require("./tree-shell-smoke-helpers"); const MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES = process.env.MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES || "0"; if (MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES !== "1") { console.log("[task500] 跳过: MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES 未设为 1 (当前值: '%s')", MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES); console.log("[task500] 设置 MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES=1 并重启 mnote-web 后再运行此 smoke。"); process.exit(0); } const PAGE_URL = `${BASE_URL}/ui-debug/components`; async function main() { console.log("[task500] 启动 UI Debug 组件矩阵 smoke …"); const browser = await chromium.launch({ headless: true }); const context = await browser.newContext(); const page = await context.newPage(); try { // 1. 认证 await ensureAuthenticated(page); console.log("[task500] ✓ 已认证"); // 2. 导航到 /ui-debug/components await page.goto(PAGE_URL, { waitUntil: "networkidle", timeout: UI_TIMEOUT_MS }); console.log("[task500] ✓ 页面已加载: %s", PAGE_URL); // 3. 页面标题 const title = await page.title(); assert(title.includes("UI Debug"), `标题应包含 "UI Debug",实际: "${title}"`); console.log("[task500] ✓ 页面标题: %s", title); // 4. 页面根元素 const root = page.locator(".ui-debug-root"); await root.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); console.log("[task500] ✓ .ui-debug-root 可见"); // 5. Button 矩阵应当可见 const matrix = page.locator(".ui-debug-matrix"); await matrix.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); const btnCount = await matrix.locator("button").count(); assert(btnCount >= 12, `按钮矩阵应至少有 12 个按钮,实际: ${btnCount}`); console.log("[task500] ✓ 按钮矩阵: %d 个按钮", btnCount); // 6. 主题切换 const darkBtn = page.locator("[data-ui-debug-theme=\"dark\"]"); await darkBtn.click(); await page.waitForTimeout(200); const theme = await root.getAttribute("data-ui-debug-theme"); assert(theme === "dark", `主题应为 dark,实际: "${theme}"`); console.log("[task500] ✓ 主题切换: dark"); // 切回 light const lightBtn = page.locator("[data-ui-debug-theme=\"light\"]"); await lightBtn.click(); await page.waitForTimeout(200); const theme2 = await root.getAttribute("data-ui-debug-theme"); assert(theme2 === "light", `主题应为 light,实际: "${theme2}"`); // 7. Dialog 对话框 const dialogTrigger = page.locator("[data-ui-debug-dialog=\"info\"]"); await dialogTrigger.click(); await page.waitForTimeout(200); const dialogOverlay = page.locator("#ui-debug-dialog-overlay"); const overlayVisible = await dialogOverlay.isVisible(); assert(overlayVisible, "对话框应可见"); const dialogTitle = await page.locator("#ui-debug-dialog-title").textContent(); assert(dialogTitle.includes("提示"), `对话框标题应包含"提示",实际: "${dialogTitle}"`); console.log("[task500] ✓ 对话框打开: %s", dialogTitle); // 关闭对话框 const closeBtn = page.locator("[data-ui-debug-dialog-close=\"true\"]").first(); await closeBtn.click(); await page.waitForTimeout(200); const hidden = await dialogOverlay.getAttribute("hidden"); assert(hidden !== null, "对话框应已关闭 (hidden)"); console.log("[task500] ✓ 对话框已关闭"); // 8. Popover 菜单 const popoverTrigger = page.locator("[data-ui-debug-popover=\"actions\"]"); await popoverTrigger.click(); await page.waitForTimeout(200); const popoverPanel = page.locator("[data-ui-debug-popover-panel=\"actions\"]"); const popoverVisible = await popoverPanel.isVisible(); assert(popoverVisible, "Popover 菜单应可见"); const menuItems = await popoverPanel.locator("[role=\"menuitem\"]").count(); assert(menuItems >= 2, `Popover 应至少有 2 个菜单项,实际: ${menuItems}`); console.log("[task500] ✓ Popover 打开: %d 个菜单项", menuItems); // 关闭 popover await page.locator("body").click({ position: { x: 10, y: 10 } }); await page.waitForTimeout(200); // 9. Toast const toastTrigger = page.locator("[data-ui-debug-toast-kind=\"success\"]").first(); await toastTrigger.click(); await page.waitForTimeout(400); const toast = page.locator(".ui-debug-toast--visible"); const toastCount = await toast.count(); assert(toastCount >= 1, `Toast 应可见,实际数量: ${toastCount}`); console.log("[task500] ✓ Toast 显示"); // 10. Input / Select / Textarea const textInput = page.locator("#ui-debug-input-text"); await textInput.waitFor({ state: "visible", timeout: UI_TIMEOUT_MS }); await textInput.fill("测试文本"); const inputValue = await textInput.inputValue(); assert(inputValue === "测试文本", `输入框值应为"测试文本",实际: "${inputValue}"`); console.log("[task500] ✓ 文本输入: %s", inputValue); const selectEl = page.locator("#ui-debug-select"); await selectEl.selectOption("b"); const selectValue = await selectEl.inputValue(); assert(selectValue === "b", `下拉选择值应为"b",实际: "${selectValue}"`); console.log("[task500] ✓ 下拉选择: %s", selectValue); const textarea = page.locator("#ui-debug-textarea"); await textarea.fill("多行\n文本"); const textareaValue = await textarea.inputValue(); assert(textareaValue === "多行\n文本", `文本区域值应包含"多行",实际: "${textareaValue}"`); console.log("[task500] ✓ 文本区域: %s", textareaValue.replace(/\n/g, "\\n")); // 11. CSS 变量检查 const cssVar = await root.evaluate((el) => { const style = getComputedStyle(el); return style.getPropertyValue("--ui-debug-primary").trim(); }); assert(cssVar.length > 0, `--ui-debug-primary 应有值,实际: "${cssVar}"`); console.log("[task500] ✓ CSS 变量 --ui-debug-primary: %s", cssVar); console.log("[task500] ✅ 全部断言通过"); } catch (error) { console.error("[task500] ✗ 失败:", error.message); try { await page.screenshot({ path: "tmp/task500-failure.png", fullPage: true }); console.log("[task500] 失败截图: tmp/task500-failure.png"); } catch (_) {} process.exitCode = 1; } finally { await browser.close(); } } main();