OBPMTest DOM API 速查¶
注入:scripts/inject-obpm-dom.js → window.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_ATTRS:fieldid / 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,再按菜单 actionContent 等 formid |
| 再次点菜单「重开」 | 实际是新建 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", "单行文本框_基础")