跳转至

长内容类覆盖核对(ui-coverage.py)

位置:deploy/nfos/tools/ui-coverage.py | 语言:python3 | 是否需要 root:否

工具性质:本地静态核对(只读 CSS + 页面 JS;只写 --out 指定的文件) 运行前提:仓库检出必须在场(加载 apps/web/tests/lint-layout.py)。可从任意位置运行(工具自动向上找到含 deploy/nfos 的祖先目录) 部署状态:随仓库检出运行,不部署到 /opt/Cy/services/system.d/nfos/


用途

逐条核对「真实页面用到的 nf-* 类 → 命中哪条显示完整性规则」。

源码 docstring(逐字引用):

没有浏览器时,这是“真实页面兜底检查”的替代品:
  1) 从真实页面 JS 里抽出它用到的 nf-* 类名(class="..." / className / 字符串字面量);
  2) 从 nf.css 里解析出“与显示完整性有关”的规则(overflow / white-space / min-width /
     max-width / max-height / overflow-wrap / word-break / text-overflow / width:max-content);
  3) 逐类列出命中的规则(选择器 + 行号 + 关键声明),并标出“无任何完整性规则覆盖”的类。

实现要点:

环节 源码行为
类名抽取 4 条正则 CLASS_RX:class="..."、class='...'、class: '...'、以及形如 'nf-a nf-b' 的字符串字面量;只保留以 nf- 开头的词元
规则解析 importlib 加载 lint-layout.py,用其 split_rules(css) + decls(body) 取声明;只保留 KEYS 里的属性(overflow / overflow-x / overflow-y / white-space / min-width / max-width / max-height / height / overflow-wrap / word-break / text-overflow / width / display / flex / min-height)
匹配 对角色的选择器做词边界匹配 (^\|[\s,>+~(])\.<类名>(?![\w-])(支持后代/逗号/子选择器等组合)
未覆盖 无匹配的类归入 uncovered 并单独输出一行,措辞为“(无完整性规则命中,通常是无害的原子类/装饰类: ...)”

典型场景

  • 新增/改写页面模块后,确认它用到的长内容容器类(表格、kv、code、弹层、抽屉、通知列表等)在 CSS 里确有显示完整性声明;
  • 排查“某一列/某块内容显示不全”时,先把该类是否被任何规则覆盖查清楚,再决定改 CSS 还是改页面结构;
  • 与 ui-keyrules.py(最终生效声明)、ui-delta.py(前后对比)、ui-audit.py(模式计数)联用,构成无浏览器时的证据链。

用法与参数

源码 docstring 给出的用法(逐字引用,.scratch/ 为升级前旧路径;本页按新位置书写,路径参数不变):

用法:
  python .scratch/ui-coverage.py --css apps/web/index/css/nf.css \
      --pages apps/web/console/js/pages/dashboard.js apps/web/online/js/pages/alerts.js ... \
      --out .scratch/ui-coverage.txt

参数来自 argparse.ArgumentParser():

参数 类型 默认值 说明
--css 字符串(文件路径) 无默认值(required=True) 被核对的 CSS 文件
--pages 字符串列表(nargs='+',空格分隔多个) 无默认值(required=True) 要核对的一个或多个页面 JS 文件
--out 字符串(文件路径) ''(空 = 写 stdout) 输出文件;给值时打印 written: <path> (<N> bytes)

无参数时的默认行为:没有默认行为 —— --css 与 --pages 都是必填,缺任一者 argparse 会报错并以退出码 2 结束。

两个路径参数都按当前工作目录解析(源码不做仓库根拼接),因此推荐在仓库根执行并传相对路径。

示例

从仓库根目录执行(页面文件按需替换为实际要核对的页面):

python deploy/nfos/tools/ui-coverage.py --css apps/web/index/css/nf.css `
  --pages apps/web/console/js/pages/dashboard.js apps/web/online/js/pages/alerts.js `
  --out .scratch/ui-coverage.txt

期望输出形态:

written: .scratch/ui-coverage.txt (12345 bytes)

正文形如:

== 真实页面「长内容」类覆盖核对 ==
css: apps/web/index/css/nf.css
css 中与显示完整性相关的规则条数: ...

----- apps/web/console/js/pages/dashboard.js (用到 12 个 nf-* 类) -----
  .nf-table                   -> 3 条规则
        L1234  .nf-table          display:table; ...
  (无完整性规则命中,通常是无害的原子类/装饰类: nf-tag, ...)
情形 期望退出码
正常完成 0(源码未调用 sys.exit)
缺 --css / --pages 2(argparse 报错)
CSS 或页面文件不存在 / --out 不可写 1(Python traceback)
lint-layout.py 缺失或接口变更(split_rules/decls 不存在) 1(importlib/AttributeError traceback)

“未覆盖的类”不影响退出码:本工具输出的是清单,不是断言。

依赖与权限

  • 语言/版本:Python 3(#!/usr/bin/env python3)。
  • 第三方依赖:无,标准库 argparse / importlib.util / io / os / re / sys。
  • 仓库依赖:必需 —— load_lint() 用 importlib.util.spec_from_file_location 加载 <repo>/apps/web/tests/lint-layout.py,并依赖其 split_rules / decls 两个函数。
  • 权限:不需要 root;不需要网络(纯离线);写权限仅用于 --out。

风险与注意事项

  • 只读输入,只写 --out:不修改 CSS、不修改页面 JS、不改 lint-layout.py。不传 --out 时只写 stdout,不落盘。
  • 类名抽取是启发式:只认 4 条正则形式;动态拼出来的类名(模板字符串、变量拼接、'nf-' + x)抽不到,会低估覆盖率。
  • “无完整性规则命中”不等于有问题:输出里已按源码措辞标注“通常是无害的原子类/装饰类”。真正需要关注的是那些本应是长内容容器却零命中的类。
  • 依赖 lint-layout.py 的当前接口:split_rules / decls 的签名或返回值变化会让本工具直接报错(这是回归信号,不是静默降级)。
  • 覆盖率统计的是“该类名出现在至少一条含完整性属性的选择器里”,不判断声明的值是否合理(例如 overflow:hidden 本身就可能正是致病写法)。

回滚

本工具不部署到线上、不改线上系统;不改动任何被核对文件。

  • 产出/改动位置:仅 --out 指定的文件(例如 .scratch/ui-coverage.txt)。
  • 清理方式:
Remove-Item -Force .scratch/ui-coverage.txt
  • 不给 --out 时无任何落盘物,无需清理;线上侧无需回滚。