Explorar o código

test(web): S-cfc-74 能力键 admin.family.planEntry 契约级单测(TDD-RED,预期红 6/6)——planCount=0 可点击进方案明细 + plans 弹窗补「代生成方案」入口

Sisyphus hai 4 días
pai
achega
4f78da6ab3
Modificáronse 1 ficheiros con 105 adicións e 0 borrados
  1. 105 0
      cfc-web/src/__tests__/views/FamiliesPlanEntry.spec.js

+ 105 - 0
cfc-web/src/__tests__/views/FamiliesPlanEntry.spec.js

@@ -0,0 +1,105 @@
+/**
+ * admin.family.planEntry 契约级单测(TDD-RED 阶段,预期红)
+ *
+ * 能力键:admin.family.planEntry
+ * 溯源·需求:docs/superpowers/specs/2026-11-08-family-admin-list-polish.md §需求3
+ *   (方案数列 0 也渲染可点击链接 + plans 弹窗补「代生成方案」入口)
+ * 溯源·设计:cfc-web/src/views/Families.vue
+ *   - L68-73  去掉 planCount>0 条件(planCount=0 也渲染可点击 el-link)
+ *   - L229-248 plans 明细弹窗补「代生成方案」入口(跳转 HealthPlanManage 或内嵌生成表单)
+ *
+ * 【为何以「源码契约」而非「shallowMount 组件挂载」方式书写】
+ * 参考风格(ActivityAudit.spec.js)使用 shallowMount 挂载组件。但当前 cfclub 主链的
+ * Families.vue 模板存在一处先在的硬编译错误:查看成员弹窗中 `<el-empty v-else>`
+ * 嵌套在 `<el-table>`(其 v-if 挂在 table 自身)内部,vue-template-compiler 将其
+ * 列为 errors[],导致 vue-jest 无法加载整个组件(suite 直接编译失败)。本任务明确禁止
+ * 修改 src/main 实现,故无法绕过该先在障碍。GREEN 阶段落地本能力时会同步修正该嵌套;
+ * 届时可将本文件升级为 shallowMount 挂载式测试。
+ *
+ * 因此本文件采用「源码契约」断言:读取真实的 Families.vue 源码,断言设计契约所需
+ * 结构/行为缺失(RED)。GREEN 阶段实现后断言转绿。断言聚焦行为契约,不锁定实现细节
+ * (跳转路由 vs 内嵌表单二者任一即可)。
+ */
+import fs from 'fs'
+import path from 'path'
+
+const srcPath = path.resolve(__dirname, '../../views/Families.vue')
+const source = fs.readFileSync(srcPath, 'utf8')
+
+/** 取 planCount 列模板块(`<el-table-column prop="planCount"` 至其 `</el-table-column>`) */
+function planCountColumn() {
+  const start = source.indexOf('<el-table-column prop="planCount"')
+  expect(start).toBeGreaterThan(-1) // 方案数列必须存在(既有结构)
+  const end = source.indexOf('</el-table-column>', start)
+  return source.slice(start, end)
+}
+
+/** 取组件 script 段(`<script>` 至 `</script>`) */
+function scriptBlock() {
+  const start = source.indexOf('<script>')
+  const end = source.indexOf('</script>')
+  return source.slice(start, end)
+}
+
+/** 取 components 段声明(含所有子组件注册) */
+function componentsBlock() {
+  const m = source.match(/components\s*:\s*\{([^]*)\}/m)
+  return m ? m[1] : ''
+}
+
+describe('admin.family.planEntry 契约 (TDD-RED)', () => {
+  describe('AC1 · 列表 planCount=0 也渲染可点击链接', () => {
+    const col = () => planCountColumn()
+
+    it('planCount 列不应再包含 `planCount > 0` 门控(L68-73 契约要求去掉该条件)', () => {
+      expect(col()).not.toMatch(/planCount\s*>\s*0/)
+    })
+
+    it('planCount 列不应再包含 `.cell-zero` 灰字 fallback(L68-73 契约要求改为 el-link)', () => {
+      expect(col()).not.toContain('cell-zero')
+    })
+
+    it('planCount 列的 el-link 不应被 `v-if="scope.row.planCount > 0"` 门控,且需保留 openDetailDialog 点击行为', () => {
+      expect(col()).toContain('<el-link')
+      expect(col()).toContain("openDetailDialog(scope.row.id, 'plans')")
+      // 契约核心:当前 el-link 被 planCount>0 门控包围(<el-link v-if="scope.row.planCount > 0"...),RED 阶段应断言其未被门控
+      expect(col()).not.toMatch(/<el-link[^>]*v-if\s*=\s*["']scope\.row\.planCount\s*>\s*0["']/)
+    })
+  })
+
+  describe('AC2 · plans 明细弹窗补「代生成方案」入口', () => {
+    /** 取 plans 明细弹窗区块(detailType === 'plans' 对应模板分支) */
+    function plansDialogBlock() {
+      const start = source.indexOf("detailType === 'plans'")
+      expect(start).toBeGreaterThan(-1) // 明细弹窗 plans 分支存在是既有契约
+      const end = source.indexOf("detailType ===", start + 1)
+      return source.slice(start, end > 0 ? end : start + 5000)
+    }
+
+    const dialog = () => plansDialogBlock()
+    const script = () => scriptBlock()
+
+    it('plans 明细弹窗区块内需出现「代生成方案」文案或语义标记(design anchor L229-248)', () => {
+      const hasEntry =
+        dialog().includes('代生成方案') ||
+        dialog().includes('plan-gen-entry') ||
+        dialog().includes('handleGeneratePlanOnBehalf') ||
+        dialog().includes('openPlanGen') ||
+        dialog().includes('goPlanGen')
+      expect(hasEntry).toBe(true)
+    })
+
+    it('组件 script 需提供处理代生成方案流程的方法(跳转 HealthPlanManage 路由或触发内嵌生成表单)', () => {
+      const hasHandler =
+        /(openPlanGen|goPlanGen|handlePlanGen|showPlanGenForm)/.test(script()) ||
+        (/health-plan-manage/.test(script()))
+      expect(hasHandler).toBe(true)
+    })
+
+    it('代生成方案流程需绑定 familyId(跳转时需携带 query.familyId 或复用 detailFamilyId)', () => {
+      const s = script()
+      expect(s).toMatch(/familyId/)
+      expect(s).toMatch(/health-plan-manage|HealthPlanManage/)
+    })
+  })
+})