|
| 1 | +# 微信小程序蓝本生成提示词(给编程AI用) |
| 2 | + |
| 3 | +> 本文档是给编程AI(Cascade/Cursor/Copilot等)的系统提示词,用于生成 TestPilot AI 的小程序测试蓝本(testpilot.json)。 |
| 4 | +> 编程AI在生成蓝本时**必须严格遵守**以下所有规则,否则执行器会报错。 |
| 5 | +
|
| 6 | +--- |
| 7 | + |
| 8 | +## 一、蓝本JSON结构 |
| 9 | + |
| 10 | +```json |
| 11 | +{ |
| 12 | + "app_name": "应用名称", |
| 13 | + "description": "应用功能描述", |
| 14 | + "base_url": "miniprogram://绝对路径/到/小程序项目目录", |
| 15 | + "version": "1.0", |
| 16 | + "platform": "miniprogram", |
| 17 | + "pages": [ |
| 18 | + { |
| 19 | + "url": "/pages/xxx/xxx", |
| 20 | + "title": "页面标题", |
| 21 | + "description": "页面功能描述", |
| 22 | + "scenarios": [ |
| 23 | + { |
| 24 | + "name": "场景名称", |
| 25 | + "description": "场景描述", |
| 26 | + "steps": [ |
| 27 | + { "action": "步骤类型", "target": "选择器", "value": "值", "expected": "预期", "description": "描述" } |
| 28 | + ] |
| 29 | + } |
| 30 | + ] |
| 31 | + } |
| 32 | + ] |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +**结构规则:** |
| 37 | +- `pages`数组:每个元素代表一个页面,包含`url`、`title`、`description`、`scenarios` |
| 38 | +- `scenarios`数组:每个元素代表一个测试场景,包含`name`、`description`、`steps` |
| 39 | +- `steps`数组:每个元素是一个测试步骤 |
| 40 | +- 每个步骤必须有`action`字段,其余字段根据action类型决定是否必填 |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## 二、可用步骤类型(共15种) |
| 45 | + |
| 46 | +### 2.1 页面导航 |
| 47 | + |
| 48 | +| action | 用途 | 必填字段 | 说明 | |
| 49 | +|--------|------|---------|------| |
| 50 | +| `navigate` | reLaunch跳页面(清空页面栈) | value=页面路径 | 如 `/pages/index/index` | |
| 51 | +| `navigate_to` | navigateTo跳页面(保留页面栈) | value=页面路径 | 可以返回上一页 | |
| 52 | +| `reset_state` | 重置全局状态+回首页 | 无(可选value自定义重置代码) | **每个场景的第一步必须是这个** | |
| 53 | + |
| 54 | +### 2.2 元素交互 |
| 55 | + |
| 56 | +| action | 用途 | 必填字段 | 说明 | |
| 57 | +|--------|------|---------|------| |
| 58 | +| `click` | 点击元素 | target=CSS选择器 | 如 `#btn-add`、`.btn-primary` | |
| 59 | +| `tap_multiple` | 连续点击N次 | target=选择器, value=次数 | 如点击加购按钮12次 | |
| 60 | +| `fill` | 输入文本 | target=选择器, value=文本 | 用于input元素 | |
| 61 | +| `scroll` | 滚动页面 | value=滚动距离(px) | 默认400 | |
| 62 | + |
| 63 | +### 2.3 数据读取和查询 |
| 64 | + |
| 65 | +| action | 用途 | 必填字段 | 说明 | |
| 66 | +|--------|------|---------|------| |
| 67 | +| `read_text` | 读取元素文本(带3次重试) | target=选择器 | 可选expected做包含断言 | |
| 68 | +| `page_query` | 查询元素(Node端automator API) | target=选择器, value=操作类型 | 操作类型见下方详解 | |
| 69 | +| `evaluate` | 在小程序端执行JavaScript | value=JS代码 | ⚠️ 有严格格式要求,见铁律 | |
| 70 | +| `call_method` | 调用页面方法 | target=方法名, value=参数JSON | 如调用 `onSearch` | |
| 71 | + |
| 72 | +### 2.4 断言 |
| 73 | + |
| 74 | +| action | 用途 | 必填字段 | 说明 | |
| 75 | +|--------|------|---------|------| |
| 76 | +| `assert_text` | 文本包含断言 | target=选择器, expected=预期文本 | 验证元素文本包含expected | |
| 77 | +| `assert_compare` | 数值比较断言 | target=选择器, value=比较表达式 | 如 `<=10`、`==0`、`>=100` | |
| 78 | + |
| 79 | +### 2.5 辅助 |
| 80 | + |
| 81 | +| action | 用途 | 必填字段 | 说明 | |
| 82 | +|--------|------|---------|------| |
| 83 | +| `screenshot` | 截图 | 无 | description会作为截图标注 | |
| 84 | +| `wait` | 等待 | value=毫秒数 | 如 `500`、`2000` | |
| 85 | + |
| 86 | +--- |
| 87 | + |
| 88 | +## 三、page_query 详解 |
| 89 | + |
| 90 | +`page_query` 在Node.js端使用 miniprogram-automator 的 `page.$` 和 `page.$$` API查询元素。 |
| 91 | + |
| 92 | +**value字段的三种操作:** |
| 93 | +- `"text"` — 读取单个元素的文本(默认) |
| 94 | +- `"count"` — 统计匹配元素的数量 |
| 95 | +- `"texts"` — 读取所有匹配元素的文本数组 |
| 96 | + |
| 97 | +**示例:** |
| 98 | +```json |
| 99 | +{"action": "page_query", "target": ".product", "value": "count", "description": "统计商品数量"} |
| 100 | +{"action": "page_query", "target": ".p-price", "value": "texts", "description": "读取所有价格"} |
| 101 | +{"action": "page_query", "target": ".btn-checkout", "value": "count", "expected": "0", "description": "验证结算按钮不存在"} |
| 102 | +``` |
| 103 | + |
| 104 | +--- |
| 105 | + |
| 106 | +## 四、⚠️ 铁律(违反必报错) |
| 107 | + |
| 108 | +### 铁律1:evaluate的代码格式 |
| 109 | + |
| 110 | +**正确格式(两种):** |
| 111 | + |
| 112 | +**简单表达式(无声明语句):** |
| 113 | +```json |
| 114 | +{"action": "evaluate", "value": "getApp().globalData.products.length"} |
| 115 | +``` |
| 116 | + |
| 117 | +**复杂逻辑(有const/let/var/for)必须用IIFE包裹:** |
| 118 | +```json |
| 119 | +{"action": "evaluate", "value": "(() => { const app=getApp(); app.globalData.cart=[]; return 'ok'; })()"} |
| 120 | +``` |
| 121 | + |
| 122 | +**❌ 错误格式(会报错):** |
| 123 | +```json |
| 124 | +{"action": "evaluate", "value": "const g = getApp().globalData; g.cart = [];"} |
| 125 | +{"action": "evaluate", "value": "var x = 1; return x;"} |
| 126 | +``` |
| 127 | + |
| 128 | +**原因:** 执行器用 `new Function(代码)` 构造函数传给 `mp.evaluate()`。裸声明语句在函数体内执行时,automator的字符串序列化会出问题。IIFE或简单表达式则没问题。 |
| 129 | + |
| 130 | +### 铁律2:小程序没有document对象 |
| 131 | + |
| 132 | +**❌ 绝对不能在evaluate里用:** |
| 133 | +- `document.querySelector()` |
| 134 | +- `document.getElementById()` |
| 135 | +- `document.getElementsByClassName()` |
| 136 | +- 任何DOM API |
| 137 | + |
| 138 | +**✅ evaluate里只能用:** |
| 139 | +- `getApp()` — 获取App实例 |
| 140 | +- `getApp().globalData` — 读写全局数据 |
| 141 | +- `getCurrentPages()` — 获取当前页面栈 |
| 142 | +- `wx.xxx` — 微信API(如 `wx.reLaunch`、`wx.navigateTo`) |
| 143 | + |
| 144 | +**✅ 查询页面元素用 `page_query` 或 `read_text`:** |
| 145 | +```json |
| 146 | +{"action": "page_query", "target": ".product", "value": "count"} |
| 147 | +{"action": "read_text", "target": "#price-1"} |
| 148 | +``` |
| 149 | + |
| 150 | +### 铁律3:每个场景必须以reset_state开头 |
| 151 | + |
| 152 | +```json |
| 153 | +{ |
| 154 | + "name": "场景N:xxx验证", |
| 155 | + "steps": [ |
| 156 | + {"action": "reset_state", "description": "重置状态回首页"}, |
| 157 | + ... 后续步骤 |
| 158 | + ] |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +`reset_state` 做了三件事: |
| 163 | +1. 清空全局状态(购物车、优惠券、地址等) |
| 164 | +2. `wx.reLaunch` 回首页 |
| 165 | +3. 等待2秒让页面渲染完成 |
| 166 | + |
| 167 | +**不加reset_state会导致:** 上一个场景的残留数据影响当前场景。 |
| 168 | + |
| 169 | +### 铁律4:跨页面测试必须用navigate_to |
| 170 | + |
| 171 | +不能在首页场景里直接读取购物车页的元素。需要: |
| 172 | +```json |
| 173 | +{"action": "navigate_to", "value": "/pages/cart/cart", "description": "跳转购物车"}, |
| 174 | +{"action": "read_text", "target": "#subtotal", "description": "读取总计"} |
| 175 | +``` |
| 176 | + |
| 177 | +### 铁律5:call_method的参数必须是JSON字符串 |
| 178 | + |
| 179 | +```json |
| 180 | +{"action": "call_method", "target": "onSearch", "value": "{\"detail\":{\"value\":\"苹果\"}}"} |
| 181 | +{"action": "call_method", "target": "onDeliveryChange", "value": "{\"detail\":{\"value\":0}}"} |
| 182 | +``` |
| 183 | + |
| 184 | +注意value是**字符串**,里面是合法JSON,双引号需要转义为`\"`。 |
| 185 | + |
| 186 | +### 铁律6:assert_compare的格式 |
| 187 | + |
| 188 | +`value` 字段格式为 `运算符+数字`(中间无空格): |
| 189 | +```json |
| 190 | +{"action": "assert_compare", "target": "#cartCount", "value": "<=10"} |
| 191 | +{"action": "assert_compare", "target": "#deliveryFee", "value": "==0"} |
| 192 | +{"action": "assert_compare", "target": "#total", "value": ">=100"} |
| 193 | +``` |
| 194 | + |
| 195 | +支持的运算符:`==`、`!=`、`<`、`<=`、`>`、`>=` |
| 196 | + |
| 197 | +### 铁律7:选择器必须是小程序wxml中实际存在的 |
| 198 | + |
| 199 | +小程序用的是CSS选择器语法,但针对的是wxml中的class和id: |
| 200 | +- `#price-1` — id选择器 |
| 201 | +- `.btn-primary` — class选择器 |
| 202 | +- `.product .p-name` — 后代选择器 |
| 203 | +- `#product-7 .btn-primary` — 组合选择器 |
| 204 | + |
| 205 | +**生成蓝本前必须先阅读wxml文件**,确认选择器存在。 |
| 206 | + |
| 207 | +--- |
| 208 | + |
| 209 | +## 五、踩坑记录(曾经导致失败的错误) |
| 210 | + |
| 211 | +### 踩坑1:evaluate用字符串传声明语句 |
| 212 | +``` |
| 213 | +错误:mp.evaluate("var g = getApp()...") |
| 214 | +报错:Unexpected token 'var' |
| 215 | +原因:automator字符串evaluate不支持声明语句 |
| 216 | +修复:用IIFE包裹 (() => { ... })() |
| 217 | +``` |
| 218 | + |
| 219 | +### 踩坑2:evaluate用字符串传URL路径 |
| 220 | +``` |
| 221 | +错误:mp.evaluate(`wx.reLaunch({ url: "${url}" })`) |
| 222 | +报错:Arg string terminates parameters early |
| 223 | +原因:URL中的/字符导致automator字符串解析中断 |
| 224 | +修复:执行器已改用new Function,蓝本里的evaluate不需要写wx.reLaunch |
| 225 | +``` |
| 226 | + |
| 227 | +### 踩坑3:evaluate里用document.querySelector |
| 228 | +``` |
| 229 | +错误:evaluate里写 document.querySelector('.price') |
| 230 | +报错:document is not defined |
| 231 | +原因:小程序环境没有DOM,不是浏览器 |
| 232 | +修复:改用page_query步骤读取元素 |
| 233 | +``` |
| 234 | + |
| 235 | +### 踩坑4:callMethod('onShow')超时 |
| 236 | +``` |
| 237 | +错误:reset_state里调用 homePage.callMethod('onShow') |
| 238 | +现象:后续步骤卡死100秒 |
| 239 | +原因:SDK方法会超时 |
| 240 | +修复:去掉callMethod,reLaunch+sleep(2000)足够 |
| 241 | +``` |
| 242 | + |
| 243 | +### 踩坑5:空购物车点不存在的按钮 |
| 244 | +``` |
| 245 | +错误:空购物车时点击 .btn-checkout |
| 246 | +报错:元素未找到: .btn-checkout |
| 247 | +原因:空购物车时wx:if条件为false,按钮不渲染 |
| 248 | +修复:用page_query检查count==0验证按钮不存在 |
| 249 | +``` |
| 250 | + |
| 251 | +### 踩坑6:场景间状态污染 |
| 252 | +``` |
| 253 | +错误:场景2没有reset_state,上一场景的购物车数据还在 |
| 254 | +现象:断言失败,数据不符合预期 |
| 255 | +修复:每个场景第一步必须reset_state |
| 256 | +``` |
| 257 | + |
| 258 | +--- |
| 259 | + |
| 260 | +## 六、蓝本生成示例任务 |
| 261 | + |
| 262 | +### 任务描述给AI的格式 |
| 263 | + |
| 264 | +``` |
| 265 | +请为以下微信小程序生成测试蓝本(testpilot.json): |
| 266 | +
|
| 267 | +项目路径:D:/projects/TestPilotAI/miniprogram-demo |
| 268 | +页面文件: |
| 269 | +- pages/index/index.wxml(首页) |
| 270 | +- pages/cart/cart.wxml(购物车) |
| 271 | +- pages/checkout/checkout.wxml(结算页) |
| 272 | +
|
| 273 | +[这里粘贴wxml和js文件的完整内容] |
| 274 | +
|
| 275 | +请严格按照 TestPilot AI 小程序蓝本格式生成,遵守所有铁律。 |
| 276 | +要求: |
| 277 | +1. 每个场景以reset_state开头 |
| 278 | +2. evaluate用IIFE格式 |
| 279 | +3. 不使用document对象 |
| 280 | +4. 选择器来自wxml中实际的id和class |
| 281 | +5. 覆盖尽可能多的业务逻辑和边界情况 |
| 282 | +``` |
| 283 | + |
| 284 | +--- |
| 285 | + |
| 286 | +## 七、附:执行器步骤类型与执行环境对照 |
| 287 | + |
| 288 | +| 步骤类型 | 执行环境 | 能访问什么 | |
| 289 | +|---------|---------|-----------| |
| 290 | +| `evaluate` | 小程序端(微信JS沙箱) | `getApp()`、`wx.xxx`、`getCurrentPages()` | |
| 291 | +| `page_query` | Node.js端(automator) | `page.$`、`page.$$`、元素的`.text()` | |
| 292 | +| `read_text` | Node.js端(automator) | `page.$` → `.text()`,带3次重试 | |
| 293 | +| `call_method` | automator桥接 | 调用页面实例方法 | |
| 294 | +| `click`/`tap_multiple` | automator桥接 | `page.$` → `.tap()` | |
| 295 | +| `navigate`/`navigate_to` | 小程序端(通过new Function) | `wx.reLaunch`/`wx.navigateTo` | |
| 296 | +| `reset_state` | 小程序端+automator | 清全局数据+reLaunch回首页 | |
| 297 | +| `assert_text`/`assert_compare` | Node.js端 | 读元素文本做断言 | |
| 298 | +| `screenshot` | automator | 截图保存到screenshots目录 | |
| 299 | + |
0 commit comments