@creatoria/miniapp-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +469 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +144 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/config/defaults.d.ts +73 -0
  7. package/dist/config/defaults.d.ts.map +1 -0
  8. package/dist/config/defaults.js +118 -0
  9. package/dist/config/defaults.js.map +1 -0
  10. package/dist/config/loader.d.ts +50 -0
  11. package/dist/config/loader.d.ts.map +1 -0
  12. package/dist/config/loader.js +189 -0
  13. package/dist/config/loader.js.map +1 -0
  14. package/dist/core/element-ref.d.ts +44 -0
  15. package/dist/core/element-ref.d.ts.map +1 -0
  16. package/dist/core/element-ref.js +213 -0
  17. package/dist/core/element-ref.js.map +1 -0
  18. package/dist/core/logger.d.ts +55 -0
  19. package/dist/core/logger.d.ts.map +1 -0
  20. package/dist/core/logger.js +378 -0
  21. package/dist/core/logger.js.map +1 -0
  22. package/dist/core/output.d.ts +21 -0
  23. package/dist/core/output.d.ts.map +1 -0
  24. package/dist/core/output.js +56 -0
  25. package/dist/core/output.js.map +1 -0
  26. package/dist/core/report-generator.d.ts +24 -0
  27. package/dist/core/report-generator.d.ts.map +1 -0
  28. package/dist/core/report-generator.js +212 -0
  29. package/dist/core/report-generator.js.map +1 -0
  30. package/dist/core/session.d.ts +83 -0
  31. package/dist/core/session.d.ts.map +1 -0
  32. package/dist/core/session.js +306 -0
  33. package/dist/core/session.js.map +1 -0
  34. package/dist/core/timeout.d.ts +49 -0
  35. package/dist/core/timeout.d.ts.map +1 -0
  36. package/dist/core/timeout.js +67 -0
  37. package/dist/core/timeout.js.map +1 -0
  38. package/dist/core/tool-logger.d.ts +83 -0
  39. package/dist/core/tool-logger.d.ts.map +1 -0
  40. package/dist/core/tool-logger.js +453 -0
  41. package/dist/core/tool-logger.js.map +1 -0
  42. package/dist/core/validation.d.ts +39 -0
  43. package/dist/core/validation.d.ts.map +1 -0
  44. package/dist/core/validation.js +93 -0
  45. package/dist/core/validation.js.map +1 -0
  46. package/dist/index.d.ts +7 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +6 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/server.d.ts +7 -0
  51. package/dist/server.d.ts.map +1 -0
  52. package/dist/server.js +85 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/tools/assert.d.ts +108 -0
  55. package/dist/tools/assert.d.ts.map +1 -0
  56. package/dist/tools/assert.js +291 -0
  57. package/dist/tools/assert.js.map +1 -0
  58. package/dist/tools/automator.d.ts +45 -0
  59. package/dist/tools/automator.d.ts.map +1 -0
  60. package/dist/tools/automator.js +186 -0
  61. package/dist/tools/automator.js.map +1 -0
  62. package/dist/tools/element.d.ts +253 -0
  63. package/dist/tools/element.d.ts.map +1 -0
  64. package/dist/tools/element.js +615 -0
  65. package/dist/tools/element.js.map +1 -0
  66. package/dist/tools/index.d.ts +97 -0
  67. package/dist/tools/index.d.ts.map +1 -0
  68. package/dist/tools/index.js +1565 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/tools/miniprogram.d.ts +79 -0
  71. package/dist/tools/miniprogram.d.ts.map +1 -0
  72. package/dist/tools/miniprogram.js +245 -0
  73. package/dist/tools/miniprogram.js.map +1 -0
  74. package/dist/tools/network.d.ts +65 -0
  75. package/dist/tools/network.d.ts.map +1 -0
  76. package/dist/tools/network.js +205 -0
  77. package/dist/tools/network.js.map +1 -0
  78. package/dist/tools/page.d.ts +108 -0
  79. package/dist/tools/page.d.ts.map +1 -0
  80. package/dist/tools/page.js +307 -0
  81. package/dist/tools/page.js.map +1 -0
  82. package/dist/tools/record.d.ts +86 -0
  83. package/dist/tools/record.d.ts.map +1 -0
  84. package/dist/tools/record.js +316 -0
  85. package/dist/tools/record.js.map +1 -0
  86. package/dist/tools/snapshot.d.ts +82 -0
  87. package/dist/tools/snapshot.d.ts.map +1 -0
  88. package/dist/tools/snapshot.js +258 -0
  89. package/dist/tools/snapshot.js.map +1 -0
  90. package/dist/types.d.ts +240 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +5 -0
  93. package/dist/types.js.map +1 -0
  94. package/docs/SIMPLE_USAGE.md +210 -0
  95. package/docs/api/README.md +244 -0
  96. package/docs/api/assert.md +1015 -0
  97. package/docs/api/automator.md +345 -0
  98. package/docs/api/element.md +1454 -0
  99. package/docs/api/miniprogram.md +558 -0
  100. package/docs/api/network.md +883 -0
  101. package/docs/api/page.md +909 -0
  102. package/docs/api/record.md +963 -0
  103. package/docs/api/snapshot.md +792 -0
  104. package/docs/architecture.E-Docs.md +1359 -0
  105. package/docs/architecture.F1.md +720 -0
  106. package/docs/architecture.F2.md +871 -0
  107. package/docs/architecture.F3.md +905 -0
  108. package/docs/architecture.md +90 -0
  109. package/docs/charter.A1.align.yaml +170 -0
  110. package/docs/charter.A2.align.yaml +199 -0
  111. package/docs/charter.A3.align.yaml +242 -0
  112. package/docs/charter.A4.align.yaml +227 -0
  113. package/docs/charter.B1.align.yaml +179 -0
  114. package/docs/charter.B2.align.yaml +200 -0
  115. package/docs/charter.B3.align.yaml +200 -0
  116. package/docs/charter.B4.align.yaml +188 -0
  117. package/docs/charter.C1.align.yaml +190 -0
  118. package/docs/charter.C2.align.yaml +202 -0
  119. package/docs/charter.C3.align.yaml +211 -0
  120. package/docs/charter.C4.align.yaml +263 -0
  121. package/docs/charter.C5.align.yaml +220 -0
  122. package/docs/charter.D1.align.yaml +190 -0
  123. package/docs/charter.D2.align.yaml +234 -0
  124. package/docs/charter.D3.align.yaml +206 -0
  125. package/docs/charter.E-Docs.align.yaml +294 -0
  126. package/docs/charter.F1.align.yaml +193 -0
  127. package/docs/charter.F2.align.yaml +248 -0
  128. package/docs/charter.F3.align.yaml +287 -0
  129. package/docs/charter.G.align.yaml +174 -0
  130. package/docs/charter.align.yaml +111 -0
  131. package/docs/examples/session-report-usage.md +449 -0
  132. package/docs/maintenance.md +682 -0
  133. package/docs/playwright-mcp/350/260/203/347/240/224.md +53 -0
  134. package/docs/setup-guide.md +775 -0
  135. package/docs/tasks.A1.atomize.md +296 -0
  136. package/docs/tasks.A2.atomize.md +408 -0
  137. package/docs/tasks.A3.atomize.md +564 -0
  138. package/docs/tasks.A4.atomize.md +496 -0
  139. package/docs/tasks.B1.atomize.md +352 -0
  140. package/docs/tasks.B2.atomize.md +561 -0
  141. package/docs/tasks.B3.atomize.md +508 -0
  142. package/docs/tasks.B4.atomize.md +504 -0
  143. package/docs/tasks.C1.atomize.md +540 -0
  144. package/docs/tasks.C2.atomize.md +665 -0
  145. package/docs/tasks.C3.atomize.md +745 -0
  146. package/docs/tasks.C4.atomize.md +908 -0
  147. package/docs/tasks.C5.atomize.md +755 -0
  148. package/docs/tasks.D1.atomize.md +547 -0
  149. package/docs/tasks.D2.atomize.md +619 -0
  150. package/docs/tasks.D3.atomize.md +790 -0
  151. package/docs/tasks.E-Docs.atomize.md +1204 -0
  152. package/docs/tasks.atomize.md +189 -0
  153. package/docs/troubleshooting.md +855 -0
  154. package/docs//345/256/214/346/225/264/345/256/236/347/216/260/346/226/271/346/241/210.md +155 -0
  155. package/docs//345/274/200/345/217/221/344/273/273/345/212/241/350/256/241/345/210/222.md +110 -0
  156. package/docs//345/276/256/344/277/241/345/260/217/347/250/213/345/272/217/350/207/252/345/212/250/345/214/226API/345/256/214/346/225/264/346/226/207/346/241/243.md +894 -0
  157. package/docs//345/276/256/344/277/241/345/260/217/347/250/213/345/272/217/350/207/252/345/212/250/345/214/226/345/256/214/346/225/264/346/223/215/344/275/234/346/211/213/345/206/214.md +1885 -0
  158. package/docs//346/216/245/345/217/243/346/226/271/346/241/210.md +565 -0
  159. package/docs//347/254/254/344/270/200/347/211/210/346/234/254/346/226/271/346/241/210.md +380 -0
  160. package/package.json +87 -0
@@ -0,0 +1,792 @@
1
+ # Snapshot API
2
+
3
+ > Snapshot 工具提供页面、应用和元素的状态捕获功能,用于调试、问题诊断和测试记录。
4
+
5
+ ## 工具列表
6
+
7
+ | 工具名称 | 描述 | 捕获内容 |
8
+ |---------|------|----------|
9
+ | `snapshot_page` | 页面快照 | 页面数据 + 页面信息 + 可选截图 |
10
+ | `snapshot_full` | 完整应用快照 | 系统信息 + 页面栈 + 当前页面 + 可选截图 |
11
+ | `snapshot_element` | 元素快照 | 元素文本 + 尺寸 + 位置 + 可选截图 |
12
+
13
+ ---
14
+
15
+ ## snapshot_page
16
+
17
+ 捕获当前页面的完整快照,包括页面数据、路径、查询参数和可选截图。
18
+
19
+ ### 参数
20
+
21
+ | 参数名 | 类型 | 必需 | 默认值 | 描述 |
22
+ |--------|------|------|--------|------|
23
+ | `pagePath` | string | ⭐ | currentPage | 页面路径(默认当前页面) |
24
+ | `filename` | string | ⭐ | 自动生成 | 文件名(不含扩展名) |
25
+ | `includeScreenshot` | boolean | ⭐ | true | 是否包含截图 |
26
+ | `fullPage` | boolean | ⭐ | false | 是否截取整个页面 |
27
+
28
+ ### 返回值
29
+
30
+ ```typescript
31
+ {
32
+ success: true,
33
+ message: "Page snapshot captured successfully",
34
+ snapshotPath: "/tmp/mcp-output/snapshot-20250102-143020.json",
35
+ screenshotPath: "/tmp/mcp-output/snapshot-20250102-143020.png",
36
+ data: {
37
+ timestamp: "2025-01-02T14:30:20.123Z",
38
+ pagePath: "/pages/product/detail",
39
+ pageData: {
40
+ productId: 12345,
41
+ productName: "iPhone 15 Pro",
42
+ price: 999,
43
+ stock: 100
44
+ },
45
+ pageQuery: {
46
+ id: "12345",
47
+ from: "list"
48
+ }
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### 输出文件
54
+
55
+ **JSON 文件** (`snapshot-YYYYMMDD-HHMMSS.json`):
56
+ ```json
57
+ {
58
+ "timestamp": "2025-01-02T14:30:20.123Z",
59
+ "pagePath": "/pages/product/detail",
60
+ "pageData": {
61
+ "productId": 12345,
62
+ "productName": "iPhone 15 Pro",
63
+ "price": 999,
64
+ "stock": 100
65
+ },
66
+ "pageQuery": {
67
+ "id": "12345",
68
+ "from": "list"
69
+ }
70
+ }
71
+ ```
72
+
73
+ **PNG 文件** (可选):
74
+ - 与 JSON 同名,扩展名为 `.png`
75
+ - 默认截取视口内容
76
+ - `fullPage: true` 时截取整个页面
77
+
78
+ ### 使用示例
79
+
80
+ ```javascript
81
+ // 示例 1: 基础快照(数据 + 截图)
82
+ const result = await snapshot_page()
83
+ console.log("快照已保存:", result.snapshotPath)
84
+ console.log("截图已保存:", result.screenshotPath)
85
+
86
+ // 示例 2: 仅保存数据,不截图
87
+ const result = await snapshot_page({
88
+ includeScreenshot: false
89
+ })
90
+ console.log("数据快照:", result.data.pageData)
91
+
92
+ // 示例 3: 全页截图
93
+ const result = await snapshot_page({
94
+ fullPage: true,
95
+ filename: "product-detail-full"
96
+ })
97
+
98
+ // 示例 4: 调试时捕获状态
99
+ try {
100
+ await element_tap({ refId: btn.refId })
101
+ await page_wait_for({ selector: ".result", timeout: 2000 })
102
+ } catch (error) {
103
+ // 错误发生时捕获快照
104
+ const snapshot = await snapshot_page({
105
+ filename: "error-state"
106
+ })
107
+ console.error("错误快照:", snapshot.snapshotPath)
108
+ throw error
109
+ }
110
+
111
+ // 示例 5: 测试记录
112
+ const tests = ["test1", "test2", "test3"]
113
+ for (const test of tests) {
114
+ // 执行测试...
115
+ await snapshot_page({
116
+ filename: `test-${test}-result`,
117
+ includeScreenshot: true
118
+ })
119
+ }
120
+
121
+ // 示例 6: 比对数据变化
122
+ const before = await snapshot_page({
123
+ filename: "before-action",
124
+ includeScreenshot: false
125
+ })
126
+ await element_tap({ refId: btn.refId })
127
+ await page_wait_for({ timeout: 500 })
128
+ const after = await snapshot_page({
129
+ filename: "after-action",
130
+ includeScreenshot: false
131
+ })
132
+ console.log("前:", before.data.pageData)
133
+ console.log("后:", after.data.pageData)
134
+ ```
135
+
136
+ ### 注意事项
137
+
138
+ - 💡 **自动命名**: 不提供 `filename` 时自动生成时间戳文件名
139
+ - 💡 **输出目录**: 文件保存在 `OUTPUT_DIR` 环境变量指定的目录(默认 `.mcp-artifacts/`)
140
+ - 💡 **JSON + PNG**: 快照数据和截图使用相同文件名(不同扩展名)
141
+ - ⚠️ **大文件**: 全页截图可能生成较大文件
142
+ - 💡 **调试利器**: 快照包含完整的页面状态,方便问题诊断
143
+
144
+ ### 相关工具
145
+
146
+ - [`snapshot_full`](#snapshot_full) - 捕获完整应用快照
147
+ - [`miniprogram_screenshot`](./miniprogram.md#miniprogram_screenshot) - 仅截图
148
+
149
+ ---
150
+
151
+ ## snapshot_full
152
+
153
+ 捕获小程序的完整应用快照,包括系统信息、页面栈、当前页面数据和可选截图。
154
+
155
+ ### 参数
156
+
157
+ | 参数名 | 类型 | 必需 | 默认值 | 描述 |
158
+ |--------|------|------|--------|------|
159
+ | `filename` | string | ⭐ | 自动生成 | 文件名(不含扩展名) |
160
+ | `includeScreenshot` | boolean | ⭐ | true | 是否包含截图 |
161
+ | `fullPage` | boolean | ⭐ | false | 是否截取整个页面 |
162
+
163
+ ### 返回值
164
+
165
+ ```typescript
166
+ {
167
+ success: true,
168
+ message: "Full application snapshot captured successfully",
169
+ snapshotPath: "/tmp/mcp-output/snapshot-20250102-143030.json",
170
+ screenshotPath: "/tmp/mcp-output/snapshot-20250102-143030.png",
171
+ data: {
172
+ timestamp: "2025-01-02T14:30:30.456Z",
173
+ systemInfo: {
174
+ model: "iPhone 15 Pro",
175
+ system: "iOS 17.2",
176
+ platform: "devtools",
177
+ version: "3.0.0",
178
+ SDKVersion: "3.3.4",
179
+ windowWidth: 375,
180
+ windowHeight: 667
181
+ },
182
+ pageStack: [
183
+ { path: "pages/index/index", query: {} },
184
+ { path: "pages/list/list", query: { category: "electronics" } },
185
+ { path: "pages/detail/detail", query: { id: "12345" } }
186
+ ],
187
+ currentPage: {
188
+ path: "pages/detail/detail",
189
+ query: { id: "12345" },
190
+ data: {
191
+ productId: 12345,
192
+ productName: "iPhone 15 Pro",
193
+ price: 999
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### 输出文件
201
+
202
+ **JSON 文件** (`snapshot-YYYYMMDD-HHMMSS.json`):
203
+ ```json
204
+ {
205
+ "timestamp": "2025-01-02T14:30:30.456Z",
206
+ "systemInfo": {
207
+ "model": "iPhone 15 Pro",
208
+ "system": "iOS 17.2",
209
+ "platform": "devtools",
210
+ "version": "3.0.0",
211
+ "SDKVersion": "3.3.4",
212
+ "windowWidth": 375,
213
+ "windowHeight": 667
214
+ },
215
+ "pageStack": [
216
+ { "path": "pages/index/index", "query": {} },
217
+ { "path": "pages/list/list", "query": { "category": "electronics" } },
218
+ { "path": "pages/detail/detail", "query": { "id": "12345" } }
219
+ ],
220
+ "currentPage": {
221
+ "path": "pages/detail/detail",
222
+ "query": { "id": "12345" },
223
+ "data": {
224
+ "productId": 12345,
225
+ "productName": "iPhone 15 Pro",
226
+ "price": 999
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ ### 使用示例
233
+
234
+ ```javascript
235
+ // 示例 1: 完整应用快照
236
+ const result = await snapshot_full()
237
+ console.log("应用快照:", result.snapshotPath)
238
+ console.log("系统信息:", result.data.systemInfo)
239
+ console.log("页面栈深度:", result.data.pageStack.length)
240
+
241
+ // 示例 2: 诊断页面栈问题
242
+ const snapshot = await snapshot_full({
243
+ filename: "page-stack-debug"
244
+ })
245
+ console.log("当前页面栈:")
246
+ snapshot.data.pageStack.forEach((page, index) => {
247
+ console.log(` ${index + 1}. ${page.path}`, page.query)
248
+ })
249
+
250
+ // 示例 3: 环境信息收集
251
+ const snapshot = await snapshot_full({
252
+ includeScreenshot: false
253
+ })
254
+ console.log("设备型号:", snapshot.data.systemInfo.model)
255
+ console.log("系统版本:", snapshot.data.systemInfo.system)
256
+ console.log("SDK 版本:", snapshot.data.systemInfo.SDKVersion)
257
+
258
+ // 示例 4: 问题报告
259
+ try {
260
+ // 执行可能出错的操作
261
+ await miniprogram_navigate({
262
+ method: "navigateTo",
263
+ url: "/pages/nonexistent/index"
264
+ })
265
+ } catch (error) {
266
+ // 捕获完整应用状态用于问题报告
267
+ const snapshot = await snapshot_full({
268
+ filename: "error-report"
269
+ })
270
+ console.error("错误发生时的应用状态:", snapshot.snapshotPath)
271
+ console.error("错误信息:", error.message)
272
+ throw error
273
+ }
274
+
275
+ // 示例 5: 自动化测试报告
276
+ async function generateTestReport() {
277
+ const snapshot = await snapshot_full({
278
+ filename: "test-report",
279
+ fullPage: true
280
+ })
281
+
282
+ return {
283
+ timestamp: snapshot.data.timestamp,
284
+ environment: {
285
+ model: snapshot.data.systemInfo.model,
286
+ system: snapshot.data.systemInfo.system
287
+ },
288
+ currentPage: snapshot.data.currentPage.path,
289
+ pageStack: snapshot.data.pageStack.map(p => p.path),
290
+ screenshot: snapshot.screenshotPath
291
+ }
292
+ }
293
+
294
+ // 示例 6: 页面栈溢出检测
295
+ const snapshot = await snapshot_full({
296
+ includeScreenshot: false
297
+ })
298
+ if (snapshot.data.pageStack.length > 8) {
299
+ console.warn("⚠️ 页面栈深度过大,可能需要优化导航逻辑")
300
+ }
301
+ ```
302
+
303
+ ### 注意事项
304
+
305
+ - 💡 **最全面**: 包含系统信息、完整页面栈和当前页面数据
306
+ - 💡 **问题诊断**: 适合用于 bug 报告和问题分析
307
+ - 💡 **性能开销**: 比 `snapshot_page` 稍慢,因为需要获取更多信息
308
+ - ⚠️ **文件大小**: 页面栈深时 JSON 文件可能较大
309
+ - 💡 **环境信息**: `systemInfo` 包含完整的设备和运行环境信息
310
+
311
+ ### 相关工具
312
+
313
+ - [`snapshot_page`](#snapshot_page) - 仅捕获当前页面
314
+ - [`miniprogram_get_system_info`](./miniprogram.md#miniprogram_get_system_info) - 获取系统信息
315
+ - [`miniprogram_get_page_stack`](./miniprogram.md#miniprogram_get_page_stack) - 获取页面栈
316
+
317
+ ---
318
+
319
+ ## snapshot_element
320
+
321
+ 捕获单个元素的快照,包括文本、尺寸、位置和可选截图。
322
+
323
+ ### 参数
324
+
325
+ | 参数名 | 类型 | 必需 | 默认值 | 描述 |
326
+ |--------|------|------|--------|------|
327
+ | `refId` | string | ✅ | - | 元素引用 ID |
328
+ | `filename` | string | ⭐ | 自动生成 | 文件名(不含扩展名) |
329
+ | `includeScreenshot` | boolean | ⭐ | false | 是否包含截图(注意:截取整个屏幕,非元素本身) |
330
+
331
+ ### 返回值
332
+
333
+ ```typescript
334
+ {
335
+ success: true,
336
+ message: "Element snapshot captured successfully",
337
+ snapshotPath: "/tmp/mcp-output/snapshot-20250102-143040.json",
338
+ screenshotPath: "/tmp/mcp-output/snapshot-20250102-143040.png", // 可选
339
+ data: {
340
+ timestamp: "2025-01-02T14:30:40.789Z",
341
+ refId: "elem_abc123",
342
+ text: "iPhone 15 Pro Max",
343
+ attributes: {}, // 空对象(当前版本)
344
+ size: {
345
+ width: 345,
346
+ height: 60
347
+ },
348
+ offset: {
349
+ left: 15,
350
+ top: 200
351
+ }
352
+ }
353
+ }
354
+ ```
355
+
356
+ ### 输出文件
357
+
358
+ **JSON 文件** (`snapshot-YYYYMMDD-HHMMSS.json`):
359
+ ```json
360
+ {
361
+ "timestamp": "2025-01-02T14:30:40.789Z",
362
+ "refId": "elem_abc123",
363
+ "text": "iPhone 15 Pro Max",
364
+ "attributes": {},
365
+ "size": {
366
+ "width": 345,
367
+ "height": 60
368
+ },
369
+ "offset": {
370
+ "left": 15,
371
+ "top": 200
372
+ }
373
+ }
374
+ ```
375
+
376
+ ### 使用示例
377
+
378
+ ```javascript
379
+ // 示例 1: 捕获元素基本信息
380
+ const element = await page_query({
381
+ selector: ".product-title",
382
+ save: true
383
+ })
384
+ const snapshot = await snapshot_element({
385
+ refId: element.refId,
386
+ filename: "product-title"
387
+ })
388
+ console.log("元素文本:", snapshot.data.text)
389
+ console.log("元素尺寸:", snapshot.data.size)
390
+ console.log("元素位置:", snapshot.data.offset)
391
+
392
+ // 示例 2: 记录元素状态变化
393
+ const button = await page_query({
394
+ selector: ".submit-btn",
395
+ save: true
396
+ })
397
+
398
+ // 点击前状态
399
+ const before = await snapshot_element({
400
+ refId: button.refId,
401
+ filename: "button-before",
402
+ includeScreenshot: true
403
+ })
404
+
405
+ await element_tap({ refId: button.refId })
406
+ await page_wait_for({ timeout: 500 })
407
+
408
+ // 点击后状态
409
+ const after = await snapshot_element({
410
+ refId: button.refId,
411
+ filename: "button-after",
412
+ includeScreenshot: true
413
+ })
414
+
415
+ console.log("点击前文本:", before.data.text)
416
+ console.log("点击后文本:", after.data.text)
417
+
418
+ // 示例 3: 验证布局
419
+ const card = await page_query({
420
+ selector: ".product-card",
421
+ save: true
422
+ })
423
+ const snapshot = await snapshot_element({
424
+ refId: card.refId
425
+ })
426
+
427
+ if (snapshot.data.size.width < 300) {
428
+ console.warn("⚠️ 卡片宽度不足")
429
+ }
430
+ if (snapshot.data.offset.top < 0) {
431
+ console.warn("⚠️ 卡片在视口外")
432
+ }
433
+
434
+ // 示例 4: 调试元素不可见问题
435
+ const element = await page_query({
436
+ selector: ".hidden-element",
437
+ save: true
438
+ })
439
+ const snapshot = await snapshot_element({
440
+ refId: element.refId,
441
+ includeScreenshot: true
442
+ })
443
+
444
+ console.log("元素快照:", snapshot.snapshotPath)
445
+ if (snapshot.data.size.width === 0 || snapshot.data.size.height === 0) {
446
+ console.log("❌ 元素不可见(尺寸为零)")
447
+ }
448
+
449
+ // 示例 5: 收集多个元素信息
450
+ const items = await page_query_all({
451
+ selector: ".product-item .title",
452
+ save: true
453
+ })
454
+
455
+ const snapshots = []
456
+ for (const [index, item] of items.elements.entries()) {
457
+ const snapshot = await snapshot_element({
458
+ refId: item.refId,
459
+ filename: `item-${index}`
460
+ })
461
+ snapshots.push({
462
+ index,
463
+ text: snapshot.data.text,
464
+ size: snapshot.data.size
465
+ })
466
+ }
467
+
468
+ console.log("商品标题:")
469
+ snapshots.forEach(s => {
470
+ console.log(` ${s.index + 1}. ${s.text} (${s.size.width}x${s.size.height})`)
471
+ })
472
+
473
+ // 示例 6: 错误诊断
474
+ try {
475
+ await assert_text({
476
+ refId: element.refId,
477
+ expected: "预期文本"
478
+ })
479
+ } catch (error) {
480
+ // 断言失败时捕获元素快照
481
+ const snapshot = await snapshot_element({
482
+ refId: element.refId,
483
+ filename: "assertion-failure",
484
+ includeScreenshot: true
485
+ })
486
+ console.error("断言失败,元素实际文本:", snapshot.data.text)
487
+ throw error
488
+ }
489
+ ```
490
+
491
+ ### 注意事项
492
+
493
+ - ⚠️ **截图范围**: `includeScreenshot: true` 时截取整个屏幕,不是单个元素
494
+ - 💡 **attributes 字段**: 当前版本为空对象,未来可能扩展
495
+ - 💡 **文本获取**: 如果元素没有文本内容,`text` 字段为 `undefined`
496
+ - 💡 **坐标系**: `offset` 相对于页面左上角,单位为物理像素(px)
497
+ - 💡 **轻量快照**: 比 `snapshot_page` 更轻量,仅关注单个元素
498
+
499
+ ### 相关工具
500
+
501
+ - [`element_get_text`](./element.md#element_get_text) - 获取元素文本
502
+ - [`element_get_size`](./element.md#element_get_size) - 获取元素尺寸
503
+ - [`element_get_offset`](./element.md#element_get_offset) - 获取元素位置
504
+
505
+ ---
506
+
507
+ ## 完整示例:自动化测试快照记录
508
+
509
+ ```javascript
510
+ // 场景:完整的测试流程快照记录
511
+ async function testWithSnapshots() {
512
+ try {
513
+ // 1. 启动并捕获初始状态
514
+ await miniprogram_launch({
515
+ projectPath: "/path/to/project"
516
+ })
517
+ await miniprogram_navigate({
518
+ method: "navigateTo",
519
+ url: "/pages/product/list"
520
+ })
521
+
522
+ const initial = await snapshot_full({
523
+ filename: "01-initial-state"
524
+ })
525
+ console.log("✅ 初始状态已捕获:", initial.snapshotPath)
526
+
527
+ // 2. 执行操作并记录页面快照
528
+ const searchInput = await page_query({
529
+ selector: ".search-input",
530
+ save: true
531
+ })
532
+ await element_input({
533
+ refId: searchInput.refId,
534
+ value: "iPhone"
535
+ })
536
+ await element_tap({ selector: ".search-btn" })
537
+
538
+ await page_wait_for({
539
+ selector: ".product-item",
540
+ timeout: 2000
541
+ })
542
+
543
+ const afterSearch = await snapshot_page({
544
+ filename: "02-search-results",
545
+ fullPage: true
546
+ })
547
+ console.log("✅ 搜索结果已捕获:", afterSearch.snapshotPath)
548
+
549
+ // 3. 捕获列表项快照
550
+ const items = await page_query_all({
551
+ selector: ".product-item",
552
+ save: true
553
+ })
554
+ console.log(`找到 ${items.count} 个商品`)
555
+
556
+ const firstItem = items.elements[0]
557
+ const itemSnapshot = await snapshot_element({
558
+ refId: firstItem.refId,
559
+ filename: "03-first-product",
560
+ includeScreenshot: true
561
+ })
562
+ console.log("✅ 第一个商品快照:", itemSnapshot.data.text)
563
+
564
+ // 4. 导航到详情页
565
+ await element_tap({ refId: firstItem.refId })
566
+ await page_wait_for({
567
+ selector: ".product-detail",
568
+ timeout: 2000
569
+ })
570
+
571
+ const detailPage = await snapshot_full({
572
+ filename: "04-product-detail",
573
+ fullPage: true
574
+ })
575
+ console.log("✅ 详情页快照:", detailPage.snapshotPath)
576
+ console.log("页面栈深度:", detailPage.data.pageStack.length)
577
+
578
+ // 5. 验证并记录关键元素
579
+ const priceElement = await page_query({
580
+ selector: ".price",
581
+ save: true
582
+ })
583
+ const priceSnapshot = await snapshot_element({
584
+ refId: priceElement.refId,
585
+ filename: "05-price-element"
586
+ })
587
+ console.log("✅ 价格:", priceSnapshot.data.text)
588
+
589
+ // 6. 测试加购操作
590
+ const addToCartBtn = await page_query({
591
+ selector: ".add-to-cart-btn",
592
+ save: true
593
+ })
594
+ await element_tap({ refId: addToCartBtn.refId })
595
+
596
+ await page_wait_for({
597
+ selector: ".cart-toast",
598
+ timeout: 1000
599
+ })
600
+
601
+ const afterCart = await snapshot_page({
602
+ filename: "06-added-to-cart"
603
+ })
604
+ console.log("✅ 加购后状态:", afterCart.snapshotPath)
605
+
606
+ // 7. 生成测试报告
607
+ const finalState = await snapshot_full({
608
+ filename: "07-final-state",
609
+ includeScreenshot: true
610
+ })
611
+
612
+ const report = {
613
+ timestamp: finalState.data.timestamp,
614
+ testSteps: [
615
+ "01-initial-state",
616
+ "02-search-results",
617
+ "03-first-product",
618
+ "04-product-detail",
619
+ "05-price-element",
620
+ "06-added-to-cart",
621
+ "07-final-state"
622
+ ],
623
+ environment: finalState.data.systemInfo,
624
+ pageStack: finalState.data.pageStack,
625
+ status: "PASSED"
626
+ }
627
+
628
+ console.log("✅ 测试完成")
629
+ console.log("测试报告:", JSON.stringify(report, null, 2))
630
+
631
+ return report
632
+
633
+ } catch (error) {
634
+ // 错误时捕获完整状态
635
+ const errorState = await snapshot_full({
636
+ filename: "error-state"
637
+ })
638
+ console.error("❌ 测试失败:", error.message)
639
+ console.error("错误状态快照:", errorState.snapshotPath)
640
+
641
+ throw error
642
+ } finally {
643
+ await miniprogram_close()
644
+ }
645
+ }
646
+
647
+ testWithSnapshots()
648
+ ```
649
+
650
+ ---
651
+
652
+ ## 故障排除
653
+
654
+ ### 问题 1: 快照文件找不到
655
+
656
+ **错误**: 快照保存成功但找不到文件
657
+
658
+ **解决方案**:
659
+ 1. 检查 `OUTPUT_DIR` 环境变量配置
660
+ 2. 查看返回的 `snapshotPath` 完整路径
661
+ 3. 确认有文件写入权限
662
+
663
+ ```javascript
664
+ const result = await snapshot_page()
665
+ console.log("快照路径:", result.snapshotPath)
666
+ // 输出: /tmp/mcp-output/snapshot-20250102-143020.json
667
+ ```
668
+
669
+ ### 问题 2: 截图包含但内容为空白
670
+
671
+ **错误**: `screenshotPath` 存在但图片为空白
672
+
673
+ **可能原因**:
674
+ 1. 页面尚未渲染完成
675
+ 2. 页面内容在视口外
676
+
677
+ **解决方案**:
678
+ ```javascript
679
+ // 等待页面渲染完成
680
+ await page_wait_for({
681
+ selector: ".main-content",
682
+ timeout: 2000
683
+ })
684
+ await page_wait_for({ timeout: 500 }) // 额外延时
685
+
686
+ // 然后再截图
687
+ const snapshot = await snapshot_page({
688
+ includeScreenshot: true
689
+ })
690
+ ```
691
+
692
+ ### 问题 3: 元素快照文本为 undefined
693
+
694
+ **错误**: `snapshot.data.text` 为 `undefined`
695
+
696
+ **可能原因**:
697
+ 元素不包含文本内容(如 `image`, `input` 等)
698
+
699
+ **解决方案**:
700
+ ```javascript
701
+ const snapshot = await snapshot_element({
702
+ refId: element.refId
703
+ })
704
+
705
+ if (snapshot.data.text === undefined) {
706
+ console.log("元素无文本内容")
707
+ // 使用其他方式获取信息
708
+ const value = await element_get_value({ refId: element.refId })
709
+ console.log("元素值:", value.value)
710
+ }
711
+ ```
712
+
713
+ ---
714
+
715
+ ## 最佳实践
716
+
717
+ ### 1. 快照文件命名
718
+
719
+ ```javascript
720
+ // ✅ 推荐:使用有意义的文件名
721
+ await snapshot_page({
722
+ filename: "product-detail-loaded"
723
+ })
724
+ await snapshot_full({
725
+ filename: "error-state-login-failure"
726
+ })
727
+ await snapshot_element({
728
+ refId: el.refId,
729
+ filename: "submit-button-disabled"
730
+ })
731
+
732
+ // ❌ 不推荐:使用默认时间戳(难以识别)
733
+ await snapshot_page() // snapshot-20250102-143020.json
734
+ ```
735
+
736
+ ### 2. 何时使用哪种快照
737
+
738
+ ```javascript
739
+ // 调试单个页面问题 → snapshot_page
740
+ await snapshot_page({
741
+ filename: "page-data-debug"
742
+ })
743
+
744
+ // 诊断页面栈或系统问题 → snapshot_full
745
+ await snapshot_full({
746
+ filename: "navigation-issue-debug"
747
+ })
748
+
749
+ // 关注特定元素 → snapshot_element
750
+ await snapshot_element({
751
+ refId: el.refId,
752
+ filename: "element-layout-debug"
753
+ })
754
+ ```
755
+
756
+ ### 3. 测试流程快照
757
+
758
+ ```javascript
759
+ // ✅ 推荐:关键步骤记录快照
760
+ async function testWorkflow() {
761
+ await snapshot_full({ filename: "step-1-initial" })
762
+ // 执行操作 1...
763
+ await snapshot_page({ filename: "step-2-after-action" })
764
+ // 执行操作 2...
765
+ await snapshot_full({ filename: "step-3-final" })
766
+ }
767
+ ```
768
+
769
+ ### 4. 错误快照
770
+
771
+ ```javascript
772
+ // ✅ 推荐:错误时捕获完整状态
773
+ try {
774
+ // 测试逻辑...
775
+ } catch (error) {
776
+ await snapshot_full({
777
+ filename: `error-${Date.now()}`,
778
+ includeScreenshot: true
779
+ })
780
+ throw error
781
+ }
782
+ ```
783
+
784
+ ---
785
+
786
+ **相关文档**:
787
+ - [MiniProgram API](./miniprogram.md) - 截图和系统信息
788
+ - [Page API](./page.md) - 页面数据获取
789
+ - [Assert API](./assert.md) - 断言验证
790
+ - [使用示例](../../examples/04-snapshot-debugging.md) - 快照调试示例
791
+
792
+ **最后更新**: 2025-10-02