declare const process: { cwd(): string }; declare function require(name: string): { readFileSync?: (path: string, encoding: string) => string; join?: (...parts: string[]) => string; }; const { readFileSync } = require("node:fs"); const { join } = require("node:path"); if (!readFileSync || !join) { throw new Error("测试运行环境缺少文件读取能力"); } const root = process.cwd(); const component = readFileSync(join(root, "src/components/DatePicker.vue"), "utf-8"); const css = readFileSync(join(root, "src/assets/styles.css"), "utf-8"); const datePages = [ "CommissionsView.vue", "MonthlyPayrollView.vue", "PayrollView.vue", "RealtimeAttendanceView.vue", "ReportsView.vue", "SalaryProfilesView.vue", ]; for (const page of datePages) { const content = readFileSync(join(root, "src/views", page), "utf-8"); assertIncludes(content, "DatePicker", `${page} 应使用统一 DatePicker 组件`); assertNotIncludes(content, "type=\"month\"", `${page} 不应再使用浏览器原生月份选择器`); assertNotIncludes(content, "type=\"date\"", `${page} 不应再使用浏览器原生日期选择器`); } assertIncludes(component, 'mode: "month" | "date"'); assertIncludes(component, "date-picker-panel"); assertIncludes(component, "", "日期面板应挂到 body,避免被父级面板裁剪"); assertIncludes(component, "panelRef", "日期面板应独立追踪自身节点,保证弹层内部点击不会被当成外部点击"); assertIncludes(component, "panelStyle", "日期面板应使用运行时坐标定位到触发按钮附近"); assertIncludes(component, "updatePanelPosition", "日期面板打开、滚动或窗口变化时应重新定位"); assertIncludes(component, "month-grid"); assertIncludes(component, "calendar-grid"); assertIncludes(component, "emit(\"change\""); assertIncludes(component, "本月"); assertIncludes(component, "今天"); const pickerBlock = cssBlock(".date-picker"); assertIncludes(pickerBlock, "position: relative;"); const triggerBlock = cssBlock(".date-picker-trigger"); assertIncludes(triggerBlock, "min-height: var(--control-height);"); assertIncludes(triggerBlock, "border: 1px solid var(--line);"); const panelBlock = cssBlock(".date-picker-panel"); assertIncludes(panelBlock, "position: fixed;"); assertIncludes(panelBlock, "max-height: min(420px, calc(100vh - 24px));"); assertIncludes(panelBlock, "overflow: auto;"); assertIncludes(panelBlock, "box-shadow: var(--shadow);"); const monthGridBlock = cssBlock(".month-grid"); assertIncludes(monthGridBlock, "grid-template-columns: repeat(4, minmax(0, 1fr));"); const calendarGridBlock = cssBlock(".calendar-grid"); assertIncludes(calendarGridBlock, "grid-template-columns: repeat(7, minmax(0, 1fr));"); function cssBlock(selector: string): string { const pattern = new RegExp(`${escapeRegExp(selector)}\\s*\\{([\\s\\S]*?)\\n\\}`, "m"); const match = css.match(pattern); if (!match) { throw new Error(`缺少样式块 ${selector}`); } return match[1]; } function assertIncludes(content: string, expected: string, message?: string): void { if (!content.includes(expected)) { throw new Error(message || `期望包含 ${expected}`); } } function assertNotIncludes(content: string, unexpected: string, message?: string): void { if (content.includes(unexpected)) { throw new Error(message || `不应包含 ${unexpected}`); } } function escapeRegExp(value: string): string { return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }