소스 검색

test(web): S-cfc-73 能力键 admin.family.reportEntry 契约级单测(TDD-RED,预期红 6/6)——reportCount=0 可点击进报告明细 + 报告弹窗补代传报告入口

Sisyphus 4 일 전
부모
커밋
d6f11b0216
1개의 변경된 파일과 100개의 추가작업 그리고 0개의 파일을 삭제
  1. 100 0
      cfc-web/src/__tests__/views/FamiliesReportEntry.spec.js

+ 100 - 0
cfc-web/src/__tests__/views/FamiliesReportEntry.spec.js

@@ -0,0 +1,100 @@
+/**
+ * admin.family.reportEntry 契约级单测(TDD-RED 阶段,预期红)
+ *
+ * 能力键: admin.family.reportEntry
+ * 溯源·需求: docs/superpowers/specs/2026-11-08-family-admin-list-polish.md §需求2
+ *   (列表上传报告数为 0 时也可点击,进入后可代传报告、查看报告)
+ * 溯源·设计: cfc-web/src/views/Families.vue
+ *   - L62-67  去掉 reportCount>0 条件(reportCount=0 也渲染可点击 el-link)
+ *   - L206-226 reports 明细弹窗补「代传报告」入口(跳转 ProxyReportUpload 或内嵌上传)
+ *
+ * 【为何以「源码契约」而非「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'
+
+// 读取真实组件源码(相对本测试文件:src/__tests__/views -> src/views)
+const srcPath = path.resolve(__dirname, '../../views/Families.vue')
+const source = fs.readFileSync(srcPath, 'utf8')
+
+/** 取 reportCount 列模板块(`<el-table-column prop="reportCount"` 至其 `</el-table-column>`) */
+function reportCountColumn() {
+  const start = source.indexOf('<el-table-column prop="reportCount"')
+  expect(start).toBeGreaterThan(-1) // 列存在是既有契约
+  const end = source.indexOf('</el-table-column>', start)
+  return source.slice(start, end)
+}
+
+/** 取报告明细弹窗的 reports 分区块(`detailType === 'reports'` 至下一个 detailType 分支开头) */
+function reportsDialogBlock() {
+  const start = source.indexOf("detailType === 'reports'")
+  expect(start).toBeGreaterThan(-1) // 明细弹窗 reports 分支存在是既有契约
+  const end = source.indexOf("detailType ===", start + 1)
+  return source.slice(start, end > 0 ? end : start + 5000)
+}
+
+/** 取组件 script 段(`<script>` 至 `</script>`) */
+function scriptBlock() {
+  const start = source.indexOf('<script>')
+  const end = source.indexOf('</script>')
+  return source.slice(start, end)
+}
+
+describe('admin.family.reportEntry 契约 (TDD-RED)', () => {
+  describe('AC1 · 列表 reportCount=0 也渲染可点击链接', () => {
+    /** 当前源码中 reportCount 列内容(含 v-if 门控与 v-else 灰字两个分支) */
+    const col = () => reportCountColumn()
+
+    it('reportCount 列不应再包含 `reportCount > 0` 门控(L62-67 契约要求去掉该条件)', () => {
+      expect(col()).not.toMatch(/reportCount\s*>\s*0/)
+    })
+
+    it('reportCount 列不应再包含 `.cell-zero` 灰字 fallback(L62-67 契约要求改为 el-link)', () => {
+      expect(col()).not.toContain('cell-zero')
+    })
+
+    it('reportCount 列的 el-link 不应再被 `v-if="scope.row.reportCount > 0"` 门控,且需保留 openDetailDialog 点击行为', () => {
+      expect(col()).toContain('<el-link')
+      expect(col()).toContain("openDetailDialog(scope.row.id, 'reports')")
+      // 契约核心:当前 el-link 被 reportCount>0 门控包围(<el-link v-if="scope.row.reportCount > 0"...),RED 阶段应断言其未被门控
+      expect(col()).not.toMatch(/<el-link[^>]*v-if\s*=\s*["']scope\.row\.reportCount\s*>\s*0["']/)
+    })
+  })
+
+  describe('AC2 · 报告明细弹窗补「代传报告」入口', () => {
+    const dialog = () => reportsDialogBlock()
+    const script = () => scriptBlock()
+
+    it('reports 明细弹窗区块内需出现「代传报告」文案或语义标记(design anchor L206-226)', () => {
+      const hasEntry =
+        dialog().includes('代传报告') ||
+        dialog().includes('proxy-report-entry') ||
+        dialog().includes('handleProxyReportUpload') ||
+        dialog().includes('goProxyReportUpload')
+      expect(hasEntry).toBe(true)
+    })
+
+    it('组件 script 需提供处理代传报告流程的方法(跳转 ProxyReportUpload 路由或触发内嵌上传)', () => {
+      const hasProxyHandler =
+        /(goProxyReportUpload|handleProxyReportUpload|openProxyReportUpload)/.test(script()) ||
+        (/report-proxy-upload/.test(script()))
+      expect(hasProxyHandler).toBe(true)
+    })
+
+    it('代传报告流程需绑定 familyId(跳转时需携带 query.familyId 或复用当前 detailFamilyId)', () => {
+      const s = script()
+      expect(s).toMatch(/familyId/)
+      expect(s).toMatch(/report-proxy-upload|ProxyReportUpload/)
+    })
+  })
+})