488 lines
30 KiB
TypeScript
488 lines
30 KiB
TypeScript
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("<DesktopLayout v-if=\"isDesktop\" />");
|
|
expect(source).toContain("<WebLayout v-else />");
|
|
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("<h1>{{ TEXT.common.appName }}</h1>");
|
|
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('<header class="sidebar-head">');
|
|
const sidebarHeadEnd = layout.indexOf("</header>", 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('<div class="sidebar-title-row">');
|
|
expect(sidebarHeadTemplate.indexOf("<h1>{{ TEXT.common.appName }}</h1>")).toBeLessThan(
|
|
sidebarHeadTemplate.indexOf('<button v-if="!study.currentStudy" class="study-switcher-trigger empty"'),
|
|
);
|
|
expect(sidebarHeadTemplate).not.toContain("study-context-badge");
|
|
expect(layout).not.toContain(".study-context-badge");
|
|
expect(layout).not.toContain(".study-name");
|
|
expect(sidebarHeadTemplate).not.toContain('@command="handleStudySwitch"');
|
|
expect(layout).not.toContain("handleStudySwitch");
|
|
expect(layout).not.toContain("`切换项目:${item.name}`");
|
|
expect(layout).not.toContain("grid-template-columns: 272px minmax(0, 1fr);");
|
|
expect(layout).not.toContain("padding: 54px 16px 14px;");
|
|
expect(layout).not.toContain("padding: 44px 12px 12px;");
|
|
});
|
|
|
|
it("renders desktop preferences as a split settings panel", () => {
|
|
const preferences = readDesktopPreferencesSource();
|
|
const desktopLayout = readDesktopLayoutSource();
|
|
const webLayout = readWebLayoutSource();
|
|
|
|
expect(preferences).toContain('class="preferences-sidebar"');
|
|
expect(preferences).toContain('class="preferences-pane"');
|
|
expect(preferences).toContain("const activeSectionId = ref<PreferenceSectionId>");
|
|
expect(preferences).toContain('grid-template-columns: 232px minmax(0, 1fr);');
|
|
expect(preferences).toContain("height: min(700px, calc(100vh - 96px));");
|
|
expect(preferences).toContain("scrollbar-gutter: stable;");
|
|
expect(preferences).toContain('{ id: "connection", label: "连接"');
|
|
expect(preferences).toContain('{ id: "appearance", label: "外观"');
|
|
expect(preferences).toContain('{ id: "notifications", label: "通知"');
|
|
expect(preferences).toContain('{ id: "updates", label: "更新"');
|
|
expect(preferences).toContain('{ id: "diagnostics", label: "诊断信息"');
|
|
expect(preferences).toContain("testDesktopServerConnection");
|
|
expect(preferences).toContain("saveDesktopServerConfig");
|
|
expect(preferences).toContain("测试连通性");
|
|
expect(preferences).not.toContain("保存服务器");
|
|
expect(preferences).toContain('ElMessage.success("服务器设置已保存")');
|
|
expect(preferences).toContain("ElMessage.error(`保存失败:${message}`)");
|
|
expect(preferences).toContain("normalizeDesktopServerUrl");
|
|
expect(preferences).toContain("确认切换服务器");
|
|
expect(preferences).toContain('<Transition name="preferences-header" mode="out-in">');
|
|
expect(preferences).toContain('<Transition name="preferences-panel" mode="out-in">');
|
|
expect(preferences).toContain('<Transition name="connection-feedback">');
|
|
expect(preferences).toContain("const selectSection = (sectionId: PreferenceSectionId)");
|
|
expect(preferences).toContain("const setConnectionPending = (message: string)");
|
|
expect(preferences).toContain("const scheduleConnectionPending = (message: string)");
|
|
expect(preferences).toContain("}, 180);");
|
|
expect(preferences).toContain('scrollTo({ top: 0, behavior: "smooth" })');
|
|
expect(preferences).not.toContain("connectionStatus.value = null");
|
|
expect(preferences).toContain("min-width: 100px;");
|
|
expect(preferences).toContain("connection-feedback-enter-active");
|
|
expect(preferences).toContain("@media (prefers-reduced-motion: reduce)");
|
|
expect(preferences).toContain(':global([data-ctms-theme="dark"] .desktop-preferences)');
|
|
expect(preferences).not.toContain(':global([data-ctms-theme="dark"]) .desktop-preferences');
|
|
expect(desktopLayout).toContain(':global([data-ctms-theme="dark"] .desktop-workbench)');
|
|
expect(desktopLayout).not.toContain(':global([data-ctms-theme="dark"]) .desktop-workbench');
|
|
expect(desktopLayout).toContain('width="940px"');
|
|
expect(webLayout).toContain('width="940px"');
|
|
expect(desktopLayout).toContain('title: "连接设置"');
|
|
expect(webLayout).toContain('title: "连接设置"');
|
|
expect(desktopLayout).not.toContain('router.push("/desktop/server-settings")');
|
|
expect(webLayout).not.toContain('router.push("/desktop/server-settings")');
|
|
expect(desktopLayout).not.toContain('width="720px"');
|
|
expect(webLayout).not.toContain('width="720px"');
|
|
});
|
|
|
|
it("keeps client diagnostics out of profile while desktop controls stay in preferences", () => {
|
|
const profile = readProfileSettingsSource();
|
|
const preferences = readDesktopPreferencesSource();
|
|
|
|
expect(profile).not.toContain("form-section--desktop");
|
|
expect(profile).not.toContain("客户端与通知");
|
|
expect(profile).not.toContain("getDesktopNotificationSubscription");
|
|
expect(profile).not.toContain("setDesktopNotificationSubscription");
|
|
expect(profile).not.toContain("checkDesktopUpdateAndPrompt");
|
|
expect(profile).not.toContain("desktopNotificationsEnabled");
|
|
expect(profile).not.toContain("form-section--diagnostics");
|
|
expect(profile).not.toContain("客户端诊断");
|
|
expect(profile).not.toContain("clientDiagnosticRows");
|
|
expect(profile).not.toContain("copyClientDiagnostics");
|
|
expect(profile).not.toContain("getAppMetadata");
|
|
expect(profile).not.toContain("getDesktopServerUrl");
|
|
expect(profile).not.toContain("clientRuntime.capabilities");
|
|
expect(profile).not.toContain("诊断信息已复制");
|
|
expect(preferences).toContain("getDesktopNotificationSubscription");
|
|
expect(preferences).toContain("checkDesktopUpdateAndPrompt");
|
|
expect(preferences).toContain("clientMetadataRows");
|
|
expect(preferences).not.toContain('{ label: "主题"');
|
|
expect(preferences).not.toContain('{ label: "服务器"');
|
|
});
|
|
|
|
it("keeps workspace tabs stable and draggable like browser tabs", () => {
|
|
const source = readDesktopLayoutSource();
|
|
const tabsStart = source.indexOf('<div class="workspace-tabs"');
|
|
const tabsEnd = source.indexOf('<main class="desktop-content"', tabsStart);
|
|
const tabsTemplate = source.slice(tabsStart, tabsEnd);
|
|
|
|
expect(tabsTemplate).toContain('@click="navigateWorkspaceTab(item.path)"');
|
|
expect(tabsTemplate).toContain('draggable="true"');
|
|
expect(tabsTemplate).toContain('@dragstart="startWorkspaceTabDrag(item.path, $event)"');
|
|
expect(tabsTemplate).toContain('@drop.prevent="dropWorkspaceTab(item.path, $event)"');
|
|
expect(source).toContain("const draggingWorkspaceTabPath = ref(\"\")");
|
|
expect(source).toContain("const navigateWorkspaceTab = (path: string) => {");
|
|
expect(source).toContain("if (activeMenu.value === path) return;");
|
|
expect(source).toContain("next[existingIndex] = { ...next[existingIndex], ...item };");
|
|
expect(source).toContain("const [moved] = next.splice(sourceIndex, 1);");
|
|
expect(source).toContain("<KeepAlive :max=\"DESKTOP_WORKSPACE_TAB_CACHE_MAX\">");
|
|
expect(source).toContain(":key=\"desktopRouteCacheKey(currentRoute)\"");
|
|
expect(source).toContain("const desktopRouteCacheKey = (currentRoute: { fullPath: string }) => currentRoute.fullPath;");
|
|
expect(source).not.toContain('<div :key="currentRoute.fullPath" class="desktop-route-shell">');
|
|
expect(tabsTemplate).not.toContain('@click="router.push(item.path)"');
|
|
expect(source).not.toContain(".slice(-7)");
|
|
expect(tabsTemplate).toContain('@contextmenu.prevent.stop="openWorkspaceTabMenu(item, $event)"');
|
|
expect(tabsTemplate).not.toContain('class="tab-group"');
|
|
expect(tabsTemplate).not.toContain("{{ item.group }}");
|
|
expect(source).toContain("const lastClosedWorkspaceTab = ref<DesktopRoutePreference | null>(null);");
|
|
expect(source).toContain("const workspaceTabMenu = ref<");
|
|
expect(source).toContain("const closeOtherWorkspaceTabs = (path: string) => {");
|
|
expect(source).toContain("const restoreLastClosedWorkspaceTab = () => {");
|
|
expect(source).toContain('id: `workspace-tab:${item.path}`');
|
|
expect(source).toContain('title: `切换标签:${item.title}`');
|
|
expect(source).toContain('title: "重新打开最近关闭标签"');
|
|
});
|
|
|
|
it("keeps desktop navigation hierarchy aligned with the web sidebar", () => {
|
|
const desktopLayout = readDesktopLayoutSource();
|
|
const navigation = readNavigationSource();
|
|
const sidebarStart = desktopLayout.indexOf('<div class="sidebar-scroll">');
|
|
const sidebarEnd = desktopLayout.indexOf("</aside>", sidebarStart);
|
|
const sidebarTemplate = desktopLayout.slice(sidebarStart, sidebarEnd);
|
|
|
|
expect(sidebarTemplate.indexOf('v-if="adminNavigationItems.length"')).toBeLessThan(
|
|
sidebarTemplate.indexOf('v-if="projectNavigationItems.length"'),
|
|
);
|
|
expect(sidebarTemplate).toContain('@click="toggleNavigationGroup(item)"');
|
|
expect(sidebarTemplate).toContain(':aria-expanded="isNavigationGroupExpanded(item)"');
|
|
expect(sidebarTemplate).toContain('v-show="isNavigationGroupExpanded(item)"');
|
|
expect(desktopLayout).toContain("const expandedNavigationGroups = ref<Set<string>>(new Set());");
|
|
expect(navigation).toContain('label: "系统设置"');
|
|
expect(navigation).toContain('label: "邮件服务"');
|
|
expect(navigation).toContain('group: "系统设置"');
|
|
expect(navigation).toContain("item.children?.length ? item.children : [item]");
|
|
});
|
|
|
|
it("keeps notification and update preferences lightweight", () => {
|
|
const serverSettings = readDesktopServerSettingsSource();
|
|
const preferences = readDesktopPreferencesSource();
|
|
const desktopUpdateManager = readDesktopUpdateManagerSource();
|
|
const desktopLayout = readDesktopLayoutSource();
|
|
|
|
expect(serverSettings).toContain("connectionDiagnostic");
|
|
expect(serverSettings).not.toContain("复制连接诊断");
|
|
expect(serverSettings).not.toContain("copyConnectionDiagnostic");
|
|
expect(serverSettings).toContain("确认切换服务器");
|
|
expect(serverSettings).toContain("切换服务器会退出当前会话并清除当前项目上下文");
|
|
expect(serverSettings).toContain("auth.logout({ rememberCurrentStudy: false })");
|
|
expect(serverSettings).not.toContain("服务器连接已确认");
|
|
expect(preferences).toContain("connectionDiagnostic");
|
|
expect(preferences).not.toContain("复制连接诊断");
|
|
expect(preferences).not.toContain("copyConnectionDiagnostic");
|
|
expect(preferences).not.toContain("服务器连通性正常");
|
|
expect(preferences).not.toContain("服务器连接已确认");
|
|
expect(preferences).toContain("auth.logout({ rememberCurrentStudy: false })");
|
|
expect(preferences).not.toContain("系统通知已开启");
|
|
expect(preferences).not.toContain("系统通知已关闭");
|
|
expect(preferences).not.toContain("已授权");
|
|
expect(preferences).toContain("showNotificationPermissionTag");
|
|
expect(preferences).toContain("showSystemNotificationProbe");
|
|
expect(preferences).toContain("sendDesktopNotificationTest");
|
|
expect(preferences).toContain("测试通知");
|
|
expect(preferences).not.toContain("通知诊断");
|
|
expect(preferences).not.toContain("更新诊断");
|
|
expect(preferences).not.toContain("listenDesktopNotificationDiagnostics");
|
|
expect(preferences).not.toContain(["@tauri-apps", "plugin-notification"].join("/"));
|
|
expect(preferences).toContain("listenDesktopUpdateStatus");
|
|
expect(preferences).toContain("promptForPendingDesktopUpdate");
|
|
expect(preferences).toContain("当前已是最新版本");
|
|
expect(desktopUpdateManager).not.toContain("当前构建未启用桌面端自动更新");
|
|
expect(desktopUpdateManager).not.toContain("该版本已选择稍后提醒");
|
|
expect(desktopUpdateManager).toContain("当前已是最新版本");
|
|
expect(desktopUpdateManager).toContain("桌面端更新检查失败,请稍后重试或联系管理员。");
|
|
expect(desktopLayout).toContain("desktopUpdateNoticeVisible");
|
|
expect(desktopLayout).toContain("listenDesktopUpdateStatus");
|
|
expect(desktopLayout).toContain("新版本 ${pending.version}");
|
|
});
|
|
|
|
it("routes user-facing file actions through feedback helpers", () => {
|
|
const helper = readFileTaskFeedbackSource();
|
|
const attachments = readAttachmentListSource();
|
|
const documentDetail = readDocumentDetailSource();
|
|
|
|
expect(helper).toContain("export const pickFilesWithFeedback");
|
|
expect(helper).toContain("export const saveFileWithFeedback");
|
|
expect(helper).toContain("export const openFileWithFeedback");
|
|
expect(helper).toContain("startDesktopActivity");
|
|
expect(helper).toContain("finishDesktopActivity");
|
|
expect(helper).toContain("desktopFileActivitiesEnabled");
|
|
expect(attachments).toContain("pickFilesWithFeedback");
|
|
expect(attachments).toContain("saveFileWithFeedback");
|
|
expect(attachments).toContain("openFileWithFeedback");
|
|
expect(attachments).toContain("startDesktopActivity");
|
|
expect(attachments).toContain('title: "下载附件"');
|
|
expect(attachments).toContain('title: "上传附件"');
|
|
expect(documentDetail).toContain("pickFilesWithFeedback");
|
|
expect(documentDetail).toContain("saveFileWithFeedback");
|
|
expect(documentDetail).toContain("openFileWithFeedback");
|
|
expect(attachments).not.toContain("openFile, pickFiles, saveFile");
|
|
expect(documentDetail).not.toContain("openFile, pickFiles, saveFile");
|
|
});
|
|
|
|
it("surfaces persistent desktop activity feedback without local task persistence", () => {
|
|
const desktopLayout = readDesktopLayoutSource();
|
|
const activityCenter = readDesktopActivityCenterSource();
|
|
const updateManager = readDesktopUpdateManagerSource();
|
|
|
|
expect(desktopLayout).toContain('popper-class="desktop-activity-popover"');
|
|
expect(desktopLayout).toContain("desktopActivities");
|
|
expect(desktopLayout).toContain("listenDesktopActivities");
|
|
expect(desktopLayout).toContain("clearFinishedDesktopActivities");
|
|
expect(desktopLayout).toContain("desktopActivityBadgeCount");
|
|
expect(activityCenter).toContain("DESKTOP_ACTIVITY_CHANGED_EVENT");
|
|
expect(activityCenter).toContain("safeActivityText");
|
|
expect(activityCenter).toContain("unsafeActivityTextPattern");
|
|
expect(activityCenter).not.toContain("localStorage");
|
|
expect(activityCenter).not.toContain("sessionStorage");
|
|
expect(updateManager).toContain('title: "检查桌面更新"');
|
|
expect(updateManager).toContain('title: "安装桌面更新"');
|
|
expect(updateManager).toContain("finishUpdateActivity");
|
|
});
|
|
|
|
it("keeps desktop business pages dense and workbench-like", () => {
|
|
const styles = readMainStyleSource();
|
|
|
|
expect(styles).toContain("/* Desktop workbench density */");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell.page");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell.ctms-page-shell");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .page-body");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .page-inner");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .overview");
|
|
expect(styles).toContain("padding: 0 !important;");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .table-card");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .hero-banner");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .faq-hero");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .page-bg-dots");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .module-placeholder-surface");
|
|
expect(styles).toContain("box-shadow: none !important;");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .el-table th.el-table__cell");
|
|
expect(styles).toContain("height: 34px;");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .el-table td.el-table__cell");
|
|
expect(styles).toContain("padding: 7px 10px;");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .el-pagination");
|
|
expect(styles).toContain(".desktop-workbench .desktop-route-shell .el-drawer__body");
|
|
expect(styles).toContain(":root[data-ctms-theme=\"dark\"] .desktop-workbench .desktop-route-shell .hero-stat");
|
|
});
|
|
|
|
it("keeps authentication styles self-contained for desktop CSP", () => {
|
|
const sources = [
|
|
readMainStyleSource(),
|
|
readLoginSource(),
|
|
readRegisterSource(),
|
|
readForgotPasswordSource(),
|
|
].join("\n");
|
|
|
|
expect(sources).not.toContain("fonts.googleapis.com");
|
|
expect(sources).not.toContain("fonts.gstatic.com");
|
|
expect(sources).not.toMatch(/@import\s+url\(["']https?:\/\//);
|
|
});
|
|
|
|
it("renders Element Plus modals with desktop-native surfaces in the desktop runtime", () => {
|
|
const styles = readMainStyleSource();
|
|
|
|
expect(styles).toContain("/* Desktop runtime modal surfaces */");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-overlay");
|
|
expect(styles).toContain("left: 0 !important;");
|
|
expect(styles).toContain("backdrop-filter: blur(12px) saturate(135%);");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-overlay-dialog");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-overlay-message-box");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-dialog,");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-message-box");
|
|
expect(styles).toContain("border-radius: 12px;");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-dialog__header");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-message-box__header");
|
|
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog");
|
|
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body");
|
|
expect(styles).toContain("body.is-desktop-runtime .desktop-preferences-dialog .el-dialog__body {\n padding: 0;");
|
|
expect(styles).toContain("body.is-desktop-runtime .el-message-box__btns");
|
|
expect(styles).toContain("body.is-desktop-runtime .dialog-fade-enter-from .el-dialog");
|
|
expect(styles).toContain("body.is-desktop-runtime .msgbox-fade-enter-from .el-message-box");
|
|
expect(styles).toContain(':root[data-ctms-theme="dark"] body.is-desktop-runtime .desktop-preferences-dialog');
|
|
expect(styles).toContain(':root[data-ctms-theme="dark"] body.is-desktop-runtime .el-dialog');
|
|
});
|
|
|
|
it("keeps first-pass desktop content pages workbench-oriented", () => {
|
|
const faq = readFaqSource();
|
|
const faqList = readFaqListSource();
|
|
const projectOverview = readProjectOverviewSource();
|
|
|
|
expect(faq).toContain("const isDesktop = isTauriRuntime();");
|
|
expect(faq).toContain(":class=\"{ 'medical-consult-page--desktop': isDesktop }\"");
|
|
expect(faq).toContain('v-if="!isDesktop" class="page-bg-dots"');
|
|
expect(faq).toContain('<h1 v-if="!isDesktop"');
|
|
expect(faq).toContain('v-else class="desktop-toolbar-meta"');
|
|
expect(faq).toContain('v-if="isDesktop" action="faq.create"');
|
|
expect(faq).toContain('<div v-if="!isDesktop" class="list-toolbar">');
|
|
expect(faq).toContain(".medical-consult-page--desktop .faq-workspace");
|
|
expect(faq).toContain("grid-template-columns: 236px minmax(0, 1fr);");
|
|
expect(faqList).toContain(":class=\"{ 'faq-list-card--desktop': isDesktop }\"");
|
|
expect(faqList).toContain("const isDesktop = isTauriRuntime();");
|
|
expect(faqList).toContain(".faq-list-card--desktop .faq-table");
|
|
expect(projectOverview).toContain(":class=\"{ 'project-overview--desktop': isDesktop }\"");
|
|
expect(projectOverview).not.toContain("overview-summary-strip");
|
|
expect(projectOverview).not.toContain("const activeCenterCount");
|
|
expect(projectOverview).toContain("desktop-attention-section");
|
|
expect(projectOverview).not.toContain("项目关注");
|
|
expect(projectOverview).not.toContain("desktop-attention-head");
|
|
expect(projectOverview).toContain('class="overview-updated-at">更新 {{ overviewUpdatedAtLabel }}</span>');
|
|
expect(projectOverview.indexOf("刷新")).toBeLessThan(projectOverview.indexOf('class="overview-updated-at"'));
|
|
expect(projectOverview.indexOf('class="overview-updated-at"')).toBeLessThan(projectOverview.indexOf('class="progress-legend"'));
|
|
expect(projectOverview).toContain("overview-workbench");
|
|
expect(projectOverview).toContain("enrollment-snapshot");
|
|
expect(projectOverview).toContain("desktop-attention-board");
|
|
expect(projectOverview).toContain("const stageStatusSummary");
|
|
expect(projectOverview).toContain("const activeStageItems");
|
|
expect(projectOverview).toContain("const attentionItems");
|
|
expect(projectOverview).toContain("grid-template-columns: minmax(620px, 1fr) minmax(300px, 360px);");
|
|
expect(projectOverview).toContain("max-height: min(360px, calc(100vh - 300px));");
|
|
expect(projectOverview).toContain("@media (max-width: 1240px)");
|
|
expect(projectOverview).toContain(".project-overview--desktop .overview-card");
|
|
expect(projectOverview).toContain(".project-overview--desktop .overview-card--enrollment");
|
|
expect(projectOverview).toContain(".project-overview--desktop :deep(.center-row)");
|
|
expect(projectOverview).toContain(".project-overview--desktop :deep(.stage-node)");
|
|
});
|
|
|
|
it("keeps desktop entry surfaces stable at the minimum window size", () => {
|
|
const login = readLoginSource();
|
|
const serverSettings = readDesktopServerSettingsSource();
|
|
const profile = readProfileSettingsSource();
|
|
const preferences = readDesktopPreferencesSource();
|
|
|
|
expect(login).toContain("@media (max-width: 1280px)");
|
|
expect(login).toContain("width: min(440px, 100%);");
|
|
expect(login).toContain("overflow-wrap: anywhere;");
|
|
expect(login).toContain("white-space: nowrap;");
|
|
expect(serverSettings).toContain("max-height: calc(100vh - 64px);");
|
|
expect(serverSettings).toContain("overflow: auto;");
|
|
expect(serverSettings).toContain(".diagnostic-grid code");
|
|
expect(profile).toContain('<div class="profile-layout">');
|
|
expect(profile).not.toContain('class="page"');
|
|
expect(profile).toContain("height: min(720px, calc(100vh - 64px));");
|
|
expect(profile).toContain("overflow: hidden;");
|
|
expect(profile).not.toContain("overflow: auto;");
|
|
expect(profile).toContain("overflow-wrap: anywhere;");
|
|
expect(preferences).toContain("flex-wrap: wrap;");
|
|
expect(preferences).toContain("overflow-wrap: anywhere;");
|
|
});
|
|
});
|