跳转至

OBPMTest DOM API 速查

注入:scripts/inject-obpm-dom.jswindow.OBPMTest(v0.2.1+)。
经 Playwright MCP:browser_evaluate / browser_run_code_unsafe 调用。

核心流程(必读)

浏览器脚本**不能**直接读本地磁盘。定位链路为:

1. 本机扫描 *.application
   python scripts/build_obpm_catalog.py "<path>/<App>.application" catalog.json

2. 注入 inject-obpm-dom.js 后加载目录
   OBPMTest.loadCatalog(<catalog.json 对象或字符串>)

3. 用 name / 显示名 / id 操作(内部先解析 catalog 得到 id,再按 id 找 DOM)
   await OBPMTest.setField("leaveDays", "1", "表单名")
   await OBPMTest.clickAction("保存", "表单名")
资源文件 提取内容
.form 表单根 id/name;templatecontext.fields[*].id/name/scope
.view 视图根 id/name
.activity 操作根 id/name/type、parentForm/parentView
.menu 菜单根 id/name、actionContent

运行时 DOM:字段多为 .baseField 上的 HTML id(常无 fieldid 属性);查找顺序见脚本 ID_ATTRSfieldid / id / data-id / … / activityid

约定

  • 必须先 loadCatalog,再调用 setField / getField / clickAction 等依赖目录的 API
  • 参数可为 字段/操作的 name、显示文案、或 id;歧义时传 formHint / parentHint,或直接用 id
  • 同名字段极常见(多表都有「文本」「备注」):读写必须带 formHint
  • 失败抛错:[OBPMTest].{api}: …
  • 打开/重开表单后:**不要**在只看到 #dy_refreshObj 时立刻读写字段;setField/getField/reopenDocument 已内置 waitForFormReady

API

API 参数 说明
loadCatalog(obj\|json) catalog 加载 build_obpm_catalog.py 产出;返回计数摘要
getCatalog() 当前 catalog 或 null
resolveField(nameOrId, formHint?) 名/id, 可选表单 返回 { id, name, formId, … }
resolveActivity(nameOrId, parentHint?) 名/id, 可选父资源 返回 activity 元数据
resolveMenu / resolveView 名/id 菜单/视图元数据
findDomById(id, options?) id 按 id 属性查找 DOM
ready() / waitForPortal(ms?) 页面就绪
waitForFormReady({fieldId?,docId?,timeoutMs?}) 可选 等到 #dy_refreshObj 且目标/任意 .baseField 挂载
openMenu(pathOrName) 路径或名 优先叶子 [title] / catalog;中间层可缺省
reopenDocument({appId,formId,docId,…}) 见下 关闭页签后按 docId 重开已保存单据
setField(nameOrId, value, formHint?) 字段, 值 catalog → id → 等 DOM → 写入
getField(nameOrId, formHint?) 字段 catalog → id → 等 DOM → 读显示值
clickAction(nameOrId, parentHint?) 操作 activity id → DOM;保存可回退 .act-btns [title]
viewSearch / viewClickRow / viewClickToolbar 视图查询/行/工具栏
openTodo / flowAction 待办 / 流程按钮
expectText / expectField / getToast 断言与提示

reopenDocument

await OBPMTest.reopenDocument({
  appId: "~~.…~~",          // 运行时 applicationId(menus/homepage API)
  formId: "__4785…",        // = menu.actionContent / #dy_refreshObj[formid]
  docId: "MT8c…--__4785…",  // 保存后的 #dy_refreshObj[docid]
  menuId: "__b9ff…",        // 可选,用于 tab id
  name: "P0 HTML编辑器_基础" // 可选页签名
});

内部:关闭所有OBPM.addTab({ actionContent, docId, _select, appId, … })waitForFormReady({ docId })

**禁止**用再次 openMenu(同一叶子) 代替本 API(会 empty 新建)。

字段类型(setField)

类型 行为
文本/多行/数字 填 value,触发 input/change
下拉 按选项文案选中
单选/多选 按选项文案;多选可用数组或逗号分隔
用户/部门 树形部门点 .dept-add-btn;用户点 .userPlaceholder / .new-dept-add-btn → 弹层 .user-dialog → 选部门/待选用户 →「确认」;失败则请求人工协助
视图选择框 MCP/脚本:点触发按钮(如「视图选中」)→ 可见 dialog 列表 → 勾选/点选一行 →「确认」→ 断言映射字段回填;**禁止**只改映射文本、禁止未关弹层就离开(见 SKILL「弹层闭环」)
下拉 Element Plus:点 .el-select__wrapper,在 .el-select__popper 内按文案选项点击

弹层闭环(执行侧)

  • 打开选择类弹层后必须走完「选中 → 确认/取消」;下一步前确认无可见 .el-dialog / .el-drawer / [role=dialog] 遮挡
  • 用例开始前若有残留弹层:先关闭再操作;关不掉则 FAIL,勿硬点被挡控件
  • setField 对用户/部门已内置「开弹层 → 选 → 确认」;视图选择框若尚无专用 API,用 MCP 按上表闭环,不得省略确认

已知陷阱(simple / portal Vue)

陷阱 处理
重开后 DOM not found for id 就绪竞态:页签/docid 先于 .baseField;用 waitForFormReady / 已内置的 get/setField
换菜单后仍操作旧表单字段 closeAllTabs,再按菜单 actionContentformid
再次点菜单「重开」 实际是新建 empty;改用 reopenDocument
openMenu("分类/中间/叶子") 找不到中间层 菜单扁平,直接 openMenu("叶子") 或点 [title="叶子"]
字段 name 歧义 / 界面标签≠name 查 catalog;setField("文本", v, "单行文本框_基础")
选项文案猜错(如单选填「甲」) .baseField[optionsscript] 或先打开下拉/单选读真实选项
el-select getField 为空 跳过空的 .el-select__input-wrapper,取有文案的 selected-item
clickAction("保存") 歧义 parentHint 表单名,或点 .act-btns [title="保存"]
Playwright browser_run_code_unsafe 读工作区文件 注入脚本/catalog 放到 ~/.playwright-mcp/ 等允许根目录

注入示例(MCP)

# 1) 生成 catalog(Agent 在本机执行)
python <skill>/scripts/build_obpm_catalog.py "D:/ws/LeaveOA.application" catalog.json

# 2) 注入脚本(大文件可用 browser_run_code_unsafe + 允许目录下的 embed 文件)
browser_evaluate: 执行 inject-obpm-dom.js 全文

# 3) 加载 catalog
browser_evaluate: OBPMTest.loadCatalog(<catalog object>)

# 4) 操作(name + formHint)
browser_evaluate: await OBPMTest.setField("文本", "验收001", "单行文本框_基础")
browser_evaluate: await OBPMTest.clickAction("保存", "单行文本框_基础")
# 5) 重开回显
browser_evaluate: await OBPMTest.reopenDocument({ appId, formId, docId })
browser_evaluate: await OBPMTest.expectField("文本", "验收001", "单行文本框_基础")