import { describe, expect, it } from "vitest"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; const readLayoutSource = () => readFileSync(resolve(__dirname, "./Layout.vue"), "utf8"); const readAppSource = () => readFileSync(resolve(__dirname, "../App.vue"), "utf8"); const readDesktopLayoutSource = () => readFileSync(resolve(__dirname, "./DesktopLayout.vue"), "utf8"); const readWebLayoutSource = () => readFileSync(resolve(__dirname, "./WebLayout.vue"), "utf8"); const readNavigationSource = () => readFileSync(resolve(__dirname, "./layout/navigation.ts"), "utf8"); const readTauriConfigSource = () => readFileSync(resolve(__dirname, "../../src-tauri/tauri.conf.json"), "utf8"); const readDesktopPreferencesSource = () => readFileSync(resolve(__dirname, "../views/DesktopPreferences.vue"), "utf8"); const readDesktopServerSettingsSource = () => readFileSync(resolve(__dirname, "../views/DesktopServerSettings.vue"), "utf8"); const readDesktopUpdateManagerSource = () => readFileSync(resolve(__dirname, "../session/desktopUpdateManager.ts"), "utf8"); const readDesktopActivityCenterSource = () => readFileSync(resolve(__dirname, "../session/desktopActivityCenter.ts"), "utf8"); const readProfileSettingsSource = () => readFileSync(resolve(__dirname, "../views/ProfileSettings.vue"), "utf8"); const readLoginSource = () => readFileSync(resolve(__dirname, "../views/Login.vue"), "utf8"); const readRegisterSource = () => readFileSync(resolve(__dirname, "../views/Register.vue"), "utf8"); const readForgotPasswordSource = () => readFileSync(resolve(__dirname, "../views/ForgotPassword.vue"), "utf8"); const readFileTaskFeedbackSource = () => readFileSync(resolve(__dirname, "../utils/fileTaskFeedback.ts"), "utf8"); const readAttachmentListSource = () => readFileSync(resolve(__dirname, "./attachments/AttachmentList.vue"), "utf8"); const readDocumentDetailSource = () => readFileSync(resolve(__dirname, "../views/documents/DocumentDetail.vue"), "utf8"); const readMainStyleSource = () => readFileSync(resolve(__dirname, "../styles/main.css"), "utf8"); const readFaqSource = () => readFileSync(resolve(__dirname, "../views/Faq.vue"), "utf8"); const readFaqListSource = () => readFileSync(resolve(__dirname, "./FaqList.vue"), "utf8"); const readProjectOverviewSource = () => readFileSync(resolve(__dirname, "../views/ia/ProjectOverview.vue"), "utf8"); describe("desktop layout shell", () => { it("routes desktop and web shells through the runtime flag", () => { const source = readLayoutSource(); const app = readAppSource(); expect(source).toContain("const isDesktop = isTauriRuntime()"); expect(source).toContain(""); expect(source).toContain(""); expect(app).toContain("const isDesktopRuntime = isTauriRuntime();"); expect(app).toContain('document.body.classList.add("is-desktop-runtime")'); expect(app).toContain('document.body.classList.remove("is-desktop-runtime")'); }); it("uses route-only desktop preference storage", () => { const source = readDesktopLayoutSource(); const webSource = readWebLayoutSource(); expect(source).toContain("readDesktopFavoriteRoutes"); expect(source).toContain("currentDesktopRoutePreference"); expect(source).toContain("toggleDesktopFavoriteRoute"); expect(source).not.toContain("readDesktopRecentRoutes"); expect(source).not.toContain("recordDesktopRecentRoute"); expect(source).not.toContain("desktopRecentRoutes"); expect(source).not.toContain("最近访问"); expect(webSource).toContain("readDesktopFavoriteRoutes"); expect(webSource).toContain("toggleDesktopFavoriteRoute"); expect(webSource).not.toContain("readDesktopRecentRoutes"); expect(webSource).not.toContain("recordDesktopRecentRoute"); expect(webSource).not.toContain("desktopRecentRoutes"); expect(webSource).not.toContain("最近访问"); }); it("shares active route mapping between web and desktop shells", () => { const webLayout = readWebLayoutSource(); const desktopLayout = readDesktopLayoutSource(); const navigation = readNavigationSource(); expect(webLayout).toContain("getActiveLayoutPath(route.path)"); expect(desktopLayout).toContain("getActiveLayoutPath(route.path)"); expect(navigation).toContain("export const getActiveLayoutPath"); }); it("keeps desktop context labels and command entry points de-duplicated", () => { const source = readDesktopLayoutSource(); expect(source).toContain("

{{ TEXT.common.appName }}

"); expect(source).toContain('const DESKTOP_ADMIN_SECTION_LABEL = "系统管理";'); expect(source).toContain('data-tauri-drag-region'); expect(source).toContain('class="toolbar-title"'); expect(source).toContain('class="toolbar-subtitle"'); expect(source).toContain("desktopToolbarTitle"); expect(source).toContain("desktopToolbarSubtitle"); expect(source).not.toContain("desktopBreadcrumbs"); expect(source).not.toContain("breadcrumb-chip"); expect(source).not.toContain("history-controls"); expect(source).not.toContain('title="后退"'); expect(source).not.toContain('title="前进"'); expect(source).not.toContain('title="刷新当前视图"'); expect(source).not.toContain("const sidebarTitle"); expect(source).not.toContain('title="搜索命令"'); expect(source).not.toContain("desktop-statusbar"); expect(source).not.toContain("status-item"); expect(source).not.toContain("accountProjectRoleLabel"); expect(source).not.toContain("projectStatusLabel"); expect(source).toContain("grid-template-rows: 52px auto minmax(0, 1fr);"); expect(source).toContain("const adminDesktopNavigationItems"); expect(source).toContain("const projectDesktopNavigationItems"); expect(source).toContain('id: "desktop:refresh"'); expect(source).toContain('shortcut: "⌘R"'); expect(source).toContain('command === "ctms.desktop.back"'); expect(source).toContain('command === "ctms.desktop.forward"'); expect(source).not.toContain("const root = study.currentStudy?.name || TEXT.menu.currentProject"); }); it("uses macOS overlay titlebar without adding window permissions", () => { const tauriConfig = readTauriConfigSource(); const layout = readDesktopLayoutSource(); expect(tauriConfig).toContain('"decorations": true'); expect(tauriConfig).toContain('"titleBarStyle": "Overlay"'); expect(tauriConfig).toContain('"hiddenTitle": true'); expect(tauriConfig).toContain('"trafficLightPosition": { "x": 16, "y": 18 }'); expect(layout).toContain("padding: 44px 10px 10px;"); expect(layout).toContain("-webkit-app-region: drag;"); expect(layout).toContain("-webkit-app-region: no-drag;"); expect(layout).not.toContain(["@tauri-apps", "api", "window"].join("/")); }); it("keeps the desktop sidebar dense enough for navigation-heavy screens", () => { const layout = readDesktopLayoutSource(); const sidebarHeadStart = layout.indexOf('", sidebarHeadStart); const sidebarHeadTemplate = layout.slice(sidebarHeadStart, sidebarHeadEnd); expect(layout).toContain("grid-template-columns: 248px minmax(0, 1fr);"); expect(layout).toContain("padding: 44px 10px 10px;"); expect(layout).toContain("grid-template-columns: auto minmax(0, 1fr);"); expect(layout).toContain("margin-top: 0;"); expect(layout).toContain("const desktopProjectSectionLabel = computed(() => study.currentStudy?.name || TEXT.menu.currentProject)"); expect(layout).toContain("{{ desktopProjectSectionLabel }}"); expect(layout).toContain('command="projectEntry"'); expect(layout).toContain("projectEntryMenuLabel"); expect(layout).toContain("openDesktopProjectEntry"); expect(layout).toContain("padding: 10px 8px 24px;"); expect(layout).toContain("margin-top: 12px;"); expect(layout).toContain("font-size: 14px;"); expect(sidebarHeadTemplate).toContain('