mobile-mcp-ai 2.7.11__tar.gz → 2.7.12__tar.gz

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 (57) hide show
  1. {mobile_mcp_ai-2.7.11/mobile_mcp_ai.egg-info → mobile_mcp_ai-2.7.12}/PKG-INFO +2 -1
  2. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/README.md +1 -0
  3. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/basic_tools_lite.py +77 -0
  4. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mcp_tools/mcp_server.py +10 -0
  5. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12/mobile_mcp_ai.egg-info}/PKG-INFO +2 -1
  6. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mobile_mcp_ai.egg-info/SOURCES.txt +10 -0
  7. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/setup.py +1 -1
  8. mobile_mcp_ai-2.7.12/tests/test_mind_cloud_my_space.py +80 -0
  9. mobile_mcp_ai-2.7.12/tests/test_mind_correct.py +73 -0
  10. mobile_mcp_ai-2.7.12/tests/test_mind_improved.py +83 -0
  11. mobile_mcp_ai-2.7.12/tests/test_mind_optimized.py +77 -0
  12. mobile_mcp_ai-2.7.12/tests/test_open_mind.py +37 -0
  13. mobile_mcp_ai-2.7.12/tests/test_priority_demo.py +81 -0
  14. mobile_mcp_ai-2.7.12/tests/test_simple.py +76 -0
  15. mobile_mcp_ai-2.7.12/tests/test_/344/270/276/346/212/245.py +136 -0
  16. mobile_mcp_ai-2.7.12/tests/test_/345/210/207/346/215/242/350/257/255/350/250/200/345/210/260English.py +158 -0
  17. mobile_mcp_ai-2.7.12/tests/test_/346/265/213/350/257/225.py +114 -0
  18. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/LICENSE +0 -0
  19. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/MANIFEST.in +0 -0
  20. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/__init__.py +0 -0
  21. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/config.py +0 -0
  22. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/__init__.py +0 -0
  23. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/device_manager.py +0 -0
  24. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/dynamic_config.py +0 -0
  25. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/ios_client_wda.py +0 -0
  26. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/ios_device_manager_wda.py +0 -0
  27. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/mobile_client.py +0 -0
  28. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/template_matcher.py +0 -0
  29. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
  30. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
  31. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
  32. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
  33. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
  34. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
  35. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/utils/__init__.py +0 -0
  36. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/utils/logger.py +0 -0
  37. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/utils/operation_history_manager.py +0 -0
  38. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/core/utils/smart_wait.py +0 -0
  39. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/docs/iOS_SETUP_GUIDE.md +0 -0
  40. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mcp_tools/__init__.py +0 -0
  41. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mobile_mcp_ai.egg-info/dependency_links.txt +0 -0
  42. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mobile_mcp_ai.egg-info/entry_points.txt +0 -0
  43. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mobile_mcp_ai.egg-info/not-zip-safe +0 -0
  44. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mobile_mcp_ai.egg-info/requires.txt +0 -0
  45. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/mobile_mcp_ai.egg-info/top_level.txt +0 -0
  46. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/requirements.txt +0 -0
  47. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/setup.cfg +0 -0
  48. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/templates/close_buttons/auto_x_0112_151217.png +0 -0
  49. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/templates/close_buttons/auto_x_0112_152037.png +0 -0
  50. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/templates/close_buttons/auto_x_0112_152840.png +0 -0
  51. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/templates/close_buttons/auto_x_0112_153256.png +0 -0
  52. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/templates/close_buttons/auto_x_0112_154847.png +0 -0
  53. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/templates/close_buttons/gray_x_stock_ad.png +0 -0
  54. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/utils/__init__.py +0 -0
  55. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/utils/logger.py +0 -0
  56. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/utils/xml_formatter.py +0 -0
  57. {mobile_mcp_ai-2.7.11 → mobile_mcp_ai-2.7.12}/utils/xml_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobile-mcp-ai
3
- Version: 2.7.11
3
+ Version: 2.7.12
4
4
  Summary: 移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)
5
5
  Home-page: https://github.com/test111ddff-hash/mobile-mcp-ai
6
6
  Author: douzi
@@ -460,6 +460,7 @@ pip install pyautogui pyperclip pygetwindow
460
460
  | 👆 | `mobile_swipe` | 滑动 |
461
461
  | ⌨️ | `mobile_press_key` | 按键 |
462
462
  | ⏱️ | `mobile_wait` | 等待 |
463
+ | ⌨️ | `mobile_hide_keyboard` | 收起键盘(登录场景必备) |
463
464
  | 📦 | `mobile_launch_app` | 启动应用 |
464
465
  | 📦 | `mobile_terminate_app` | 终止应用 |
465
466
  | 📦 | `mobile_list_apps` | 列出应用 |
@@ -382,6 +382,7 @@ pip install pyautogui pyperclip pygetwindow
382
382
  | 👆 | `mobile_swipe` | 滑动 |
383
383
  | ⌨️ | `mobile_press_key` | 按键 |
384
384
  | ⏱️ | `mobile_wait` | 等待 |
385
+ | ⌨️ | `mobile_hide_keyboard` | 收起键盘(登录场景必备) |
385
386
  | 📦 | `mobile_launch_app` | 启动应用 |
386
387
  | 📦 | `mobile_terminate_app` | 终止应用 |
387
388
  | 📦 | `mobile_list_apps` | 列出应用 |
@@ -2310,6 +2310,83 @@ class BasicMobileToolsLite:
2310
2310
  except Exception as e:
2311
2311
  return {"success": False, "message": f"❌ 按键失败: {e}"}
2312
2312
 
2313
+ async def hide_keyboard(self) -> Dict:
2314
+ """收起键盘
2315
+
2316
+ 在输入完成后收起键盘,确保页面元素不被键盘遮挡。
2317
+ 对于登录场景中需要勾选协议复选框非常有用。
2318
+
2319
+ Returns:
2320
+ 包含操作结果的字典
2321
+ """
2322
+ try:
2323
+ if self._is_ios():
2324
+ ios_client = self._get_ios_client()
2325
+ if ios_client and hasattr(ios_client, 'wda'):
2326
+ # iOS: 尝试点击键盘上的"完成"/"Done"按钮或发送回车收起键盘
2327
+ try:
2328
+ # 尝试点击键盘上的"完成"或"Done"按钮
2329
+ keyboard = ios_client.wda(className='XCUIElementTypeKeyboard')
2330
+ if keyboard.exists:
2331
+ # 尝试找 return/done/完成 按钮
2332
+ for btn_name in ['return', 'Return', 'done', 'Done', '完成']:
2333
+ try:
2334
+ btn = keyboard.buttons[btn_name]
2335
+ if btn.exists:
2336
+ btn.click()
2337
+ return {"success": True, "message": "✅ 键盘已收起 (iOS - 点击完成按钮)"}
2338
+ except:
2339
+ continue
2340
+
2341
+ # 如果没有找到按钮,尝试点击键盘以外的区域
2342
+ size = ios_client.wda.window_size()
2343
+ # 点击屏幕顶部区域(通常不会被键盘遮挡)
2344
+ ios_client.wda.click(size[0] // 2, 50)
2345
+ return {"success": True, "message": "✅ 键盘已收起 (iOS - 点击空白区域)"}
2346
+ else:
2347
+ return {"success": True, "message": "💡 键盘未显示,无需收起"}
2348
+ except Exception as e:
2349
+ # 退回到发送回车键
2350
+ ios_client.wda.send_keys('\n')
2351
+ return {"success": True, "message": f"✅ 键盘已收起 (iOS - 发送回车,原因: {e})"}
2352
+ return {"success": False, "message": "❌ iOS 客户端未初始化"}
2353
+ else:
2354
+ # Android: 使用 back 键或 adb 命令收起键盘
2355
+ try:
2356
+ # 方法1: 使用 adb shell 检测键盘状态
2357
+ # u2.shell() 返回 ShellResponse(output, exit_code)
2358
+ shell_result = self.client.u2.shell('dumpsys input_method | grep mInputShown')
2359
+ # 提取 output 字段(兼容字符串和 ShellResponse 两种情况)
2360
+ result = shell_result.output if hasattr(shell_result, 'output') else str(shell_result)
2361
+
2362
+ if 'mInputShown=true' in result:
2363
+ # 键盘正在显示,按返回键收起
2364
+ self.client.u2.shell('input keyevent 4') # KEYCODE_BACK
2365
+ time.sleep(0.3)
2366
+
2367
+ # 验证键盘是否已收起
2368
+ shell_result_after = self.client.u2.shell('dumpsys input_method | grep mInputShown')
2369
+ result_after = shell_result_after.output if hasattr(shell_result_after, 'output') else str(shell_result_after)
2370
+
2371
+ if 'mInputShown=false' in result_after:
2372
+ return {"success": True, "message": "✅ 键盘已收起 (Android - back键)"}
2373
+ else:
2374
+ # 如果 back 键没效果,尝试点击空白区域
2375
+ info = self.client.u2.info
2376
+ height = info.get('displayHeight', 1920)
2377
+ width = info.get('displayWidth', 1080)
2378
+ # 点击标题栏区域
2379
+ self.client.u2.click(width // 2, 100)
2380
+ return {"success": True, "message": "✅ 键盘已收起 (Android - 点击空白区域)"}
2381
+ else:
2382
+ return {"success": True, "message": "💡 键盘未显示,无需收起"}
2383
+ except Exception as e:
2384
+ # 备用方案:直接按返回键
2385
+ self.client.u2.shell('input keyevent 4')
2386
+ return {"success": True, "message": f"✅ 键盘收起完成 (Android - back键,备用方案: {e})"}
2387
+ except Exception as e:
2388
+ return {"success": False, "message": f"❌ 收起键盘失败: {e}"}
2389
+
2313
2390
  def wait(self, seconds: float) -> Dict:
2314
2391
  """等待指定时间"""
2315
2392
  time.sleep(seconds)
@@ -594,6 +594,12 @@ class MobileMCPServer:
594
594
  }
595
595
  ))
596
596
 
597
+ tools.append(Tool(
598
+ name="mobile_hide_keyboard",
599
+ description="⌨️ 收起键盘。【重要】输入密码后勾选协议前必须调用,确保复选框不被键盘遮挡。登录场景必备!",
600
+ inputSchema={"type": "object", "properties": {}, "required": []}
601
+ ))
602
+
597
603
  # ==================== 应用管理 ====================
598
604
  tools.append(Tool(
599
605
  name="mobile_launch_app",
@@ -1040,6 +1046,10 @@ class MobileMCPServer:
1040
1046
  result = self.tools.wait(arguments["seconds"])
1041
1047
  return [TextContent(type="text", text=self.format_response(result))]
1042
1048
 
1049
+ elif name == "mobile_hide_keyboard":
1050
+ result = await self.tools.hide_keyboard()
1051
+ return [TextContent(type="text", text=self.format_response(result))]
1052
+
1043
1053
  # 应用管理
1044
1054
  elif name == "mobile_launch_app":
1045
1055
  result = await self.tools.launch_app(arguments["package_name"])
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobile-mcp-ai
3
- Version: 2.7.11
3
+ Version: 2.7.12
4
4
  Summary: 移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)
5
5
  Home-page: https://github.com/test111ddff-hash/mobile-mcp-ai
6
6
  Author: douzi
@@ -460,6 +460,7 @@ pip install pyautogui pyperclip pygetwindow
460
460
  | 👆 | `mobile_swipe` | 滑动 |
461
461
  | ⌨️ | `mobile_press_key` | 按键 |
462
462
  | ⏱️ | `mobile_wait` | 等待 |
463
+ | ⌨️ | `mobile_hide_keyboard` | 收起键盘(登录场景必备) |
463
464
  | 📦 | `mobile_launch_app` | 启动应用 |
464
465
  | 📦 | `mobile_terminate_app` | 终止应用 |
465
466
  | 📦 | `mobile_list_apps` | 列出应用 |
@@ -65,6 +65,16 @@ templates/close_buttons/auto_x_0112_152840.png
65
65
  templates/close_buttons/auto_x_0112_153256.png
66
66
  templates/close_buttons/auto_x_0112_154847.png
67
67
  templates/close_buttons/gray_x_stock_ad.png
68
+ tests/test_mind_cloud_my_space.py
69
+ tests/test_mind_correct.py
70
+ tests/test_mind_improved.py
71
+ tests/test_mind_optimized.py
72
+ tests/test_open_mind.py
73
+ tests/test_priority_demo.py
74
+ tests/test_simple.py
75
+ tests/test_举报.py
76
+ tests/test_切换语言到English.py
77
+ tests/test_测试.py
68
78
  utils/__init__.py
69
79
  utils/logger.py
70
80
  utils/xml_formatter.py
@@ -25,7 +25,7 @@ if requirements_file.exists():
25
25
 
26
26
  setup(
27
27
  name="mobile-mcp-ai",
28
- version="2.7.11", # 统一使用预置条件字段管理前置依赖,支持从账号配置表格读取账号信息
28
+ version="2.7.12", # 新增 hide_keyboard 工具,用于登录场景收起键盘确保协议复选框可点击
29
29
  author="douzi",
30
30
  author_email="1492994674@qq.com",
31
31
  description="移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)",
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: Mind云文档我的空间
5
+ 生成时间: 2025-12-17 11:00:00
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+ # 广告关闭按钮关键词(可自定义)
13
+ AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
14
+
15
+
16
+ def smart_wait(d, timeout=10):
17
+ """智能等待页面稳定"""
18
+ d.implicitly_wait(timeout)
19
+ time.sleep(0.5) # 额外等待动画
20
+
21
+
22
+ def close_ad_if_exists(d):
23
+ """尝试关闭广告弹窗"""
24
+ for keyword in AD_CLOSE_KEYWORDS:
25
+ elem = d(textContains=keyword)
26
+ if elem.exists(timeout=0.5):
27
+ try:
28
+ elem.click()
29
+ print(f' 📢 关闭广告: {keyword}')
30
+ time.sleep(0.5)
31
+ return True
32
+ except:
33
+ pass
34
+ return False
35
+
36
+
37
+ def safe_click(d, selector, timeout=5):
38
+ """安全点击(带等待和重试)"""
39
+ try:
40
+ if selector.exists(timeout=timeout):
41
+ selector.click()
42
+ return True
43
+ return False
44
+ except Exception as e:
45
+ print(f' ⚠️ 点击失败: {e}')
46
+ return False
47
+
48
+
49
+ def test_main():
50
+ # 连接设备
51
+ d = u2.connect()
52
+ d.implicitly_wait(10) # 设置全局等待
53
+
54
+ # 启动应用
55
+ d.app_start(PACKAGE_NAME)
56
+ smart_wait(d)
57
+
58
+ # 尝试关闭启动广告
59
+ close_ad_if_exists(d)
60
+
61
+ # 步骤1: 点击文本 'Mind'
62
+ safe_click(d, d(text='Mind'))
63
+ smart_wait(d)
64
+ close_ad_if_exists(d) # 检查广告
65
+
66
+ # 步骤2: 点击坐标 (756, 2277)
67
+ d.click(756, 2277)
68
+ smart_wait(d)
69
+ close_ad_if_exists(d) # 检查广告
70
+
71
+ # 步骤3: 点击坐标 (815, 285)
72
+ d.click(815, 285)
73
+ smart_wait(d)
74
+ close_ad_if_exists(d) # 检查广告
75
+
76
+ print('✅ 测试完成')
77
+
78
+
79
+ if __name__ == '__main__':
80
+ test_main()
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: Mind云文档我的空间_正确版
5
+ 生成时间: 2025-12-17 11:08:10
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+ # 广告关闭按钮关键词(可自定义)
13
+ AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
14
+
15
+
16
+ def smart_wait(d, seconds=1):
17
+ """等待页面稳定"""
18
+ time.sleep(seconds)
19
+
20
+
21
+ def close_ad_if_exists(d, quick=False):
22
+ """尝试关闭广告弹窗(quick=True 时只检查常见的)"""
23
+ keywords = AD_CLOSE_KEYWORDS[:3] if quick else AD_CLOSE_KEYWORDS
24
+ for keyword in keywords:
25
+ elem = d(textContains=keyword)
26
+ if elem.exists(timeout=0.3): # 缩短超时
27
+ try:
28
+ elem.click()
29
+ print(f' 📢 关闭广告: {keyword}')
30
+ time.sleep(0.3)
31
+ return True
32
+ except:
33
+ pass
34
+ return False
35
+
36
+
37
+ def safe_click(d, selector, timeout=3):
38
+ """安全点击(带等待)"""
39
+ try:
40
+ if selector.exists(timeout=timeout):
41
+ selector.click()
42
+ return True
43
+ return False
44
+ except Exception as e:
45
+ print(f' ⚠️ 点击失败: {e}')
46
+ return False
47
+
48
+
49
+ def test_main():
50
+ # 连接设备
51
+ d = u2.connect()
52
+ d.implicitly_wait(10) # 设置全局等待
53
+
54
+ # 启动应用
55
+ d.app_start(PACKAGE_NAME)
56
+ smart_wait(d)
57
+
58
+ # 尝试关闭启动广告
59
+ close_ad_if_exists(d)
60
+
61
+ # 步骤1: 点击坐标 (756, 2277)
62
+ d.click(756, 2277)
63
+ time.sleep(0.5) # 等待响应
64
+
65
+ # 步骤2: 点击坐标 (815, 285)
66
+ d.click(815, 285)
67
+ time.sleep(0.5) # 等待响应
68
+
69
+ print('✅ 测试完成')
70
+
71
+
72
+ if __name__ == '__main__':
73
+ test_main()
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: 打开Mind应用测试
5
+ 生成时间: 2025-12-17 10:52:37
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+ # 广告关闭按钮关键词(可自定义)
13
+ AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
14
+
15
+
16
+ def smart_wait(d, timeout=10):
17
+ """智能等待页面稳定"""
18
+ d.implicitly_wait(timeout)
19
+ time.sleep(0.5) # 额外等待动画
20
+
21
+
22
+ def close_ad_if_exists(d):
23
+ """尝试关闭广告弹窗"""
24
+ for keyword in AD_CLOSE_KEYWORDS:
25
+ elem = d(textContains=keyword)
26
+ if elem.exists(timeout=0.5):
27
+ try:
28
+ elem.click()
29
+ print(f' 📢 关闭广告: {keyword}')
30
+ time.sleep(0.5)
31
+ return True
32
+ except:
33
+ pass
34
+ return False
35
+
36
+
37
+ def safe_click(d, selector, timeout=5):
38
+ """安全点击(带等待和重试)"""
39
+ try:
40
+ if selector.exists(timeout=timeout):
41
+ selector.click()
42
+ return True
43
+ return False
44
+ except Exception as e:
45
+ print(f' ⚠️ 点击失败: {e}')
46
+ return False
47
+
48
+
49
+ def test_main():
50
+ # 连接设备
51
+ d = u2.connect()
52
+ d.implicitly_wait(10) # 设置全局等待
53
+
54
+ # 启动应用
55
+ d.app_start(PACKAGE_NAME)
56
+ smart_wait(d)
57
+
58
+ # 尝试关闭启动广告
59
+ close_ad_if_exists(d)
60
+
61
+ # 步骤1: 点击文本 'Mind'
62
+ safe_click(d, d(text='Mind'))
63
+ smart_wait(d)
64
+ close_ad_if_exists(d) # 检查广告
65
+
66
+ # 步骤2: 点击坐标 (540, 1200)
67
+ d.click(540, 1200)
68
+ smart_wait(d)
69
+ close_ad_if_exists(d) # 检查广告
70
+
71
+ # 步骤3: 输入文本 '测试'
72
+ d(resourceId='com.im30.mind:id/search').set_text('测试')
73
+ smart_wait(d)
74
+
75
+ # 步骤4: 滑动 up
76
+ d.swipe_ext('up')
77
+ smart_wait(d)
78
+
79
+ print('✅ 测试完成')
80
+
81
+
82
+ if __name__ == '__main__':
83
+ test_main()
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: Mind云文档我的空间_优化版
5
+ 生成时间: 2025-12-17 11:04:53
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+ # 广告关闭按钮关键词(可自定义)
13
+ AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
14
+
15
+
16
+ def smart_wait(d, seconds=1):
17
+ """等待页面稳定"""
18
+ time.sleep(seconds)
19
+
20
+
21
+ def close_ad_if_exists(d, quick=False):
22
+ """尝试关闭广告弹窗(quick=True 时只检查常见的)"""
23
+ keywords = AD_CLOSE_KEYWORDS[:3] if quick else AD_CLOSE_KEYWORDS
24
+ for keyword in keywords:
25
+ elem = d(textContains=keyword)
26
+ if elem.exists(timeout=0.3): # 缩短超时
27
+ try:
28
+ elem.click()
29
+ print(f' 📢 关闭广告: {keyword}')
30
+ time.sleep(0.3)
31
+ return True
32
+ except:
33
+ pass
34
+ return False
35
+
36
+
37
+ def safe_click(d, selector, timeout=3):
38
+ """安全点击(带等待)"""
39
+ try:
40
+ if selector.exists(timeout=timeout):
41
+ selector.click()
42
+ return True
43
+ return False
44
+ except Exception as e:
45
+ print(f' ⚠️ 点击失败: {e}')
46
+ return False
47
+
48
+
49
+ def test_main():
50
+ # 连接设备
51
+ d = u2.connect()
52
+ d.implicitly_wait(10) # 设置全局等待
53
+
54
+ # 启动应用
55
+ d.app_start(PACKAGE_NAME)
56
+ smart_wait(d)
57
+
58
+ # 尝试关闭启动广告
59
+ close_ad_if_exists(d)
60
+
61
+ # 步骤1: 点击文本 'Mind'
62
+ safe_click(d, d(text='Mind'))
63
+ time.sleep(0.5) # 等待响应
64
+
65
+ # 步骤2: 点击坐标 (756, 2277)
66
+ d.click(756, 2277)
67
+ time.sleep(0.5) # 等待响应
68
+
69
+ # 步骤3: 点击坐标 (815, 285)
70
+ d.click(815, 285)
71
+ time.sleep(0.5) # 等待响应
72
+
73
+ print('✅ 测试完成')
74
+
75
+
76
+ if __name__ == '__main__':
77
+ test_main()
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: 打开Mind应用测试
5
+ 生成时间: 2025-12-17 10:50:37
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+
13
+ def test_main():
14
+ # 连接设备
15
+ d = u2.connect()
16
+
17
+ # 启动应用
18
+ d.app_start(PACKAGE_NAME)
19
+ time.sleep(3)
20
+
21
+ # 步骤1: 点击文本 Mind
22
+ d(text='Mind').click()
23
+ time.sleep(1)
24
+
25
+ # 步骤2: 点击坐标
26
+ d.click(540, 1200)
27
+ time.sleep(1)
28
+
29
+ # 步骤3: 输入文本
30
+ d(resourceId='com.im30.mind:id/search').set_text('测试')
31
+ time.sleep(1)
32
+
33
+ print('✅ 测试完成')
34
+
35
+
36
+ if __name__ == '__main__':
37
+ test_main()
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: 优先文本ID_坐标兜底
5
+ 生成时间: 2025-12-17 11:11:12
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+ # 广告关闭按钮关键词(可自定义)
13
+ AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
14
+
15
+
16
+ def smart_wait(d, seconds=1):
17
+ """等待页面稳定"""
18
+ time.sleep(seconds)
19
+
20
+
21
+ def close_ad_if_exists(d, quick=False):
22
+ """尝试关闭广告弹窗(quick=True 时只检查常见的)"""
23
+ keywords = AD_CLOSE_KEYWORDS[:3] if quick else AD_CLOSE_KEYWORDS
24
+ for keyword in keywords:
25
+ elem = d(textContains=keyword)
26
+ if elem.exists(timeout=0.3): # 缩短超时
27
+ try:
28
+ elem.click()
29
+ print(f' 📢 关闭广告: {keyword}')
30
+ time.sleep(0.3)
31
+ return True
32
+ except:
33
+ pass
34
+ return False
35
+
36
+
37
+ def safe_click(d, selector, timeout=3):
38
+ """安全点击(带等待)"""
39
+ try:
40
+ if selector.exists(timeout=timeout):
41
+ selector.click()
42
+ return True
43
+ return False
44
+ except Exception as e:
45
+ print(f' ⚠️ 点击失败: {e}')
46
+ return False
47
+
48
+
49
+ def test_main():
50
+ # 连接设备
51
+ d = u2.connect()
52
+ d.implicitly_wait(10) # 设置全局等待
53
+
54
+ # 启动应用(等待 3 秒让启动页/广告加载)
55
+ d.app_start(PACKAGE_NAME)
56
+ time.sleep(3) # 等待启动页/广告
57
+
58
+ # 尝试关闭启动广告(最多尝试 3 次)
59
+ for _ in range(3):
60
+ if close_ad_if_exists(d):
61
+ time.sleep(1) # 关闭广告后等待
62
+ else:
63
+ break
64
+
65
+ # 步骤1: 点击文本 '云文档'
66
+ safe_click(d, d(text='云文档'))
67
+ time.sleep(0.5) # 等待响应
68
+
69
+ # 步骤2: 点击元素 我的空间
70
+ safe_click(d, d(resourceId='com.im30.mind:id/tab_my_space'))
71
+ time.sleep(0.5) # 等待响应
72
+
73
+ # 步骤3: 点击坐标 (某个按钮)
74
+ d.click(500, 800)
75
+ time.sleep(0.5) # 等待响应
76
+
77
+ print('✅ 测试完成')
78
+
79
+
80
+ if __name__ == '__main__':
81
+ test_main()
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 测试用例: 简化版脚本
5
+ 生成时间: 2025-12-17 11:12:48
6
+ """
7
+ import time
8
+ import uiautomator2 as u2
9
+
10
+ PACKAGE_NAME = "com.im30.mind"
11
+
12
+ # === 配置(根据 App 情况调整)===
13
+ LAUNCH_WAIT = 3 # 启动后等待时间(秒)
14
+ CLOSE_AD_ON_LAUNCH = True # 是否尝试关闭启动广告
15
+ AD_CLOSE_KEYWORDS = ['关闭', '跳过', 'Skip', 'Close', '×', 'X', '我知道了', '稍后再说']
16
+
17
+
18
+ def smart_wait(d, seconds=1):
19
+ """等待页面稳定"""
20
+ time.sleep(seconds)
21
+
22
+
23
+ def close_ad_if_exists(d, quick=False):
24
+ """尝试关闭广告弹窗(quick=True 时只检查常见的)"""
25
+ keywords = AD_CLOSE_KEYWORDS[:3] if quick else AD_CLOSE_KEYWORDS
26
+ for keyword in keywords:
27
+ elem = d(textContains=keyword)
28
+ if elem.exists(timeout=0.3): # 缩短超时
29
+ try:
30
+ elem.click()
31
+ print(f' 📢 关闭广告: {keyword}')
32
+ time.sleep(0.3)
33
+ return True
34
+ except:
35
+ pass
36
+ return False
37
+
38
+
39
+ def safe_click(d, selector, timeout=3):
40
+ """安全点击(带等待)"""
41
+ try:
42
+ if selector.exists(timeout=timeout):
43
+ selector.click()
44
+ return True
45
+ return False
46
+ except Exception as e:
47
+ print(f' ⚠️ 点击失败: {e}')
48
+ return False
49
+
50
+
51
+ def test_main():
52
+ # 连接设备
53
+ d = u2.connect()
54
+ d.implicitly_wait(10) # 设置全局等待
55
+
56
+ # 启动应用
57
+ d.app_start(PACKAGE_NAME)
58
+ time.sleep(LAUNCH_WAIT) # 等待启动(可调整)
59
+
60
+ # 尝试关闭启动广告(可选,根据 App 情况调整)
61
+ if CLOSE_AD_ON_LAUNCH:
62
+ close_ad_if_exists(d)
63
+
64
+ # 步骤1: 点击文本 '云文档'
65
+ safe_click(d, d(text='云文档'))
66
+ time.sleep(0.5) # 等待响应
67
+
68
+ # 步骤2: 点击文本 '我的空间'
69
+ safe_click(d, d(text='我的空间'))
70
+ time.sleep(0.5) # 等待响应
71
+
72
+ print('✅ 测试完成')
73
+
74
+
75
+ if __name__ == '__main__':
76
+ test_main()
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 移动端测试用例: 举报测试
5
+ 生成时间: 2025-11-25 15:45:38
6
+
7
+ ⚠️ 注意:此脚本基于AI执行历史生成,使用已验证的定位方式
8
+ 如果页面结构变化,可能需要重新生成脚本
9
+ 📊 执行统计:
10
+ - 总操作数: 9
11
+ - 成功操作: 8
12
+ - 失败尝试: 1
13
+ - 成功率: 88.9%
14
+
15
+ 💡 说明:此脚本经过多次尝试后生成,只包含最终成功的操作步骤
16
+
17
+ 运行方式:
18
+ pytest 举报测试.py -v
19
+ pytest 举报测试.py --alluredir=./allure-results # 生成allure报告
20
+ """
21
+ import asyncio
22
+ import pytest
23
+ import pytest_asyncio
24
+ import sys
25
+ from pathlib import Path
26
+
27
+ # 添加backend目录到路径
28
+ # tests目录结构: backend/mobile_mcp/tests/test_xxx.py
29
+ # 需要导入: backend/mobile_mcp/core/mobile_client.py
30
+ sys.path.insert(0, str(Path(__file__).parent.parent))
31
+
32
+ from mobile_mcp.core.mobile_client import MobileClient
33
+
34
+
35
+ PACKAGE_NAME = "com.im30.way"
36
+
37
+
38
+ @pytest_asyncio.fixture(scope='function')
39
+ async def mobile_client():
40
+ """
41
+ pytest fixture: 创建并返回MobileClient实例
42
+ scope='function': 每个测试函数都会创建一个新的client
43
+ """
44
+ client = MobileClient(device_id=None)
45
+
46
+ # 启动App
47
+ print(f"\n📱 启动App: {{PACKAGE_NAME}}")
48
+ result = await client.launch_app(PACKAGE_NAME, wait_time=5)
49
+ if not result.get('success'):
50
+ raise Exception(f"启动App失败: {{result.get('reason')}}")
51
+
52
+ await asyncio.sleep(2) # 等待页面加载
53
+
54
+ yield client
55
+
56
+ # 清理
57
+ client.device_manager.disconnect()
58
+
59
+
60
+ @pytest.mark.asyncio
61
+ async def test_举报测试(mobile_client):
62
+ """
63
+ 测试用例: 举报测试
64
+
65
+ Args:
66
+ mobile_client: pytest fixture,已启动App的MobileClient实例
67
+ """
68
+ client = mobile_client
69
+
70
+ print("=" * 60)
71
+ print(f"🚀 举报测试")
72
+ print("=" * 60)
73
+
74
+ try:
75
+ # 步骤1: 点击 [810,2186][1080,2356]
76
+ print(f"\n步骤1: 点击 [810,2186][1080,2356]")
77
+ # ✅ 使用bounds坐标(已验证)
78
+ await client.click("[810,2186][1080,2356]", ref="[810,2186][1080,2356]", verify=False)
79
+ print(f"✅ 点击成功(bounds: [810,2186][1080,2356])")
80
+ await asyncio.sleep(1.5) # 等待页面响应
81
+ # 步骤2: 点击 [919,113][1034,205]
82
+ print(f"\n步骤2: 点击 [919,113][1034,205]")
83
+ # ✅ 使用bounds坐标(已验证)
84
+ await client.click("[919,113][1034,205]", ref="[919,113][1034,205]", verify=False)
85
+ print(f"✅ 点击成功(bounds: [919,113][1034,205])")
86
+ await asyncio.sleep(1.5) # 等待页面响应
87
+ # 步骤3: 点击 [861,131][919,188]
88
+ print(f"\n步骤3: 点击 [861,131][919,188]")
89
+ # ✅ 使用bounds坐标(已验证)
90
+ await client.click("[861,131][919,188]", ref="[861,131][919,188]", verify=False)
91
+ print(f"✅ 点击成功(bounds: [861,131][919,188])")
92
+ await asyncio.sleep(1.5) # 等待页面响应
93
+ # 步骤4: 点击 举报
94
+ print(f"\n步骤4: 点击 举报")
95
+ # ✅ 使用bounds坐标(已验证)
96
+ await client.click("举报", ref="[515,1557][565,1607]", verify=False)
97
+ print(f"✅ 点击成功(bounds: [515,1557][565,1607])")
98
+ await asyncio.sleep(1.5) # 等待页面响应
99
+ # 步骤5: 点击 [0,1333][1080,1460]
100
+ print(f"\n步骤5: 点击 [0,1333][1080,1460]")
101
+ # ✅ 使用bounds坐标(已验证)
102
+ await client.click("[0,1333][1080,1460]", ref="[0,1333][1080,1460]", verify=False)
103
+ print(f"✅ 点击成功(bounds: [0,1333][1080,1460])")
104
+ await asyncio.sleep(1.5) # 等待页面响应
105
+ # 步骤6: 点击 [81,292][999,826]
106
+ print(f"\n步骤6: 点击 [81,292][999,826]")
107
+ # ✅ 使用bounds坐标(已验证)
108
+ await client.click("[81,292][999,826]", ref="[81,292][999,826]", verify=False)
109
+ print(f"✅ 点击成功(bounds: [81,292][999,826])")
110
+ await asyncio.sleep(1.5) # 等待页面响应
111
+ # 步骤7: 在[81,292][999,826]输入 举报自动化测试
112
+ print(f"\n步骤7: 在[81,292][999,826]输入 举报自动化测试")
113
+ # ✅ 使用bounds坐标输入(已验证)
114
+ await client.type_text("[81,292][999,826]", "举报自动化测试", ref="[81,292][999,826]")
115
+ print(f"✅ 输入成功(bounds: [81,292][999,826])")
116
+ await asyncio.sleep(1) # 等待输入完成
117
+ # 步骤8: 点击 提交
118
+ print(f"\n步骤8: 点击 提交")
119
+ # ✅ 使用bounds坐标(已验证)
120
+ await client.click("提交", ref="[515,1003][565,1053]", verify=False)
121
+ print(f"✅ 点击成功(bounds: [515,1003][565,1053])")
122
+ await asyncio.sleep(1.5) # 等待页面响应
123
+
124
+ print("\n✅ 测试完成!")
125
+
126
+ except AssertionError as e:
127
+ print(f"\n❌ 断言失败: {e}")
128
+ # 打印当前页面快照以便调试
129
+ snapshot = await client.snapshot()
130
+ print(f"\n当前页面快照:\n{snapshot[:500]}...")
131
+ raise
132
+ except Exception as e:
133
+ print(f"\n❌ 测试失败: {e}")
134
+ import traceback
135
+ traceback.print_exc()
136
+ raise
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 移动端测试用例: 切换语言到English
5
+ 生成时间: 2025-11-24 16:42:21
6
+
7
+ ⚠️ 注意:此脚本基于AI执行历史生成,使用已验证的定位方式
8
+ 如果页面结构变化,可能需要重新生成脚本
9
+
10
+ 运行方式:
11
+ pytest 切换语言到English.py -v
12
+ pytest 切换语言到English.py --alluredir=./allure-results # 生成allure报告
13
+ """
14
+ import asyncio
15
+ import pytest
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ # 添加backend目录到路径
20
+ # tests目录结构: backend/mobile_mcp/tests/test_xxx.py
21
+ # 需要导入: backend/mobile_mcp/core/mobile_client.py
22
+ sys.path.insert(0, str(Path(__file__).parent.parent))
23
+
24
+ from mobile_mcp.core.mobile_client import MobileClient
25
+
26
+
27
+ PACKAGE_NAME = "com.im30.way"
28
+
29
+
30
+ @pytest.fixture(scope='function')
31
+ async def mobile_client():
32
+ """
33
+ pytest fixture: 创建并返回MobileClient实例
34
+ scope='function': 每个测试函数都会创建一个新的client
35
+ """
36
+ client = MobileClient(device_id=None)
37
+
38
+ # 启动App
39
+ print(f"\n📱 启动App: {{PACKAGE_NAME}}")
40
+ result = await client.launch_app(PACKAGE_NAME, wait_time=5)
41
+ if not result.get('success'):
42
+ raise Exception(f"启动App失败: {{result.get('reason')}}")
43
+
44
+ await asyncio.sleep(2) # 等待页面加载
45
+
46
+ yield client
47
+
48
+ # 清理
49
+ client.device_manager.disconnect()
50
+
51
+
52
+ @pytest.mark.asyncio
53
+ async def test_切换语言到english(mobile_client):
54
+ """
55
+ 测试用例: 切换语言到English
56
+
57
+ Args:
58
+ mobile_client: pytest fixture,已启动App的MobileClient实例
59
+ """
60
+ client = mobile_client
61
+
62
+ print("=" * 60)
63
+ print(f"🚀 切换语言到English")
64
+ print("=" * 60)
65
+
66
+ try:
67
+ # 步骤1: 点击 [810,2186][1080,2356]
68
+ print(f"\n步骤1: 点击 [810,2186][1080,2356]")
69
+ # ✅ 使用bounds坐标(已验证)
70
+ await client.click("[810,2186][1080,2356]", ref="[810,2186][1080,2356]", verify=False)
71
+ print(f"✅ 点击成功(bounds: [810,2186][1080,2356])")
72
+ await asyncio.sleep(1.5) # 等待页面响应
73
+ # 步骤2: 点击 右上角图标
74
+ print(f"\n步骤2: 点击 右上角图标")
75
+ # ✅ 使用bounds坐标(已验证)
76
+ await client.click("右上角图标", ref="[861,131][919,188]", verify=False)
77
+ print(f"✅ 点击成功(bounds: [861,131][919,188])")
78
+ await asyncio.sleep(1.5) # 等待页面响应
79
+ # 步骤3: 点击 设置
80
+ print(f"\n步骤3: 点击 设置")
81
+ # ✅ 使用text/description定位(已验证)
82
+ await client.click("设置", ref="设置", verify=False)
83
+ print(f"✅ 点击成功(text/desc: 设置)")
84
+ await asyncio.sleep(1.5) # 等待页面响应
85
+ # 步骤4: 点击 语言
86
+ print(f"\n步骤4: 点击 语言")
87
+ # ✅ 使用bounds坐标(已验证)
88
+ await client.click("语言", ref="[515,1170][565,1220]", verify=False)
89
+ print(f"✅ 点击成功(bounds: [515,1170][565,1220])")
90
+ await asyncio.sleep(1.5) # 等待页面响应
91
+ # 步骤5: 点击 语言
92
+ print(f"\n步骤5: 点击 语言")
93
+ # ✅ 使用bounds坐标(已验证)
94
+ await client.click("语言", ref="[515,1170][565,1220]", verify=False)
95
+ print(f"✅ 点击成功(bounds: [515,1170][565,1220])")
96
+ await asyncio.sleep(1.5) # 等待页面响应
97
+ # 步骤6: 点击 [810,2186][1080,2356]
98
+ print(f"\n步骤6: 点击 [810,2186][1080,2356]")
99
+ # ✅ 使用bounds坐标(已验证)
100
+ await client.click("[810,2186][1080,2356]", ref="[810,2186][1080,2356]", verify=False)
101
+ print(f"✅ 点击成功(bounds: [810,2186][1080,2356])")
102
+ await asyncio.sleep(1.5) # 等待页面响应
103
+ # 步骤7: 点击 右上角设置
104
+ print(f"\n步骤7: 点击 右上角设置")
105
+ # ✅ 使用bounds坐标(已验证)
106
+ await client.click("右上角设置", ref="[919,113][1034,205]", verify=False)
107
+ print(f"✅ 点击成功(bounds: [919,113][1034,205])")
108
+ await asyncio.sleep(1.5) # 等待页面响应
109
+ # 步骤8: 点击 [861,131][919,188]
110
+ print(f"\n步骤8: 点击 [861,131][919,188]")
111
+ # ✅ 使用bounds坐标(已验证)
112
+ await client.click("[861,131][919,188]", ref="[861,131][919,188]", verify=False)
113
+ print(f"✅ 点击成功(bounds: [861,131][919,188])")
114
+ await asyncio.sleep(1.5) # 等待页面响应
115
+ # 步骤9: 点击 语言
116
+ print(f"\n步骤9: 点击 语言")
117
+ # ✅ 使用bounds坐标(已验证)
118
+ await client.click("语言", ref="[515,1170][565,1220]", verify=False)
119
+ print(f"✅ 点击成功(bounds: [515,1170][565,1220])")
120
+ await asyncio.sleep(1.5) # 等待页面响应
121
+ # 步骤10: 点击 [0,1075][1080,1202]
122
+ print(f"\n步骤10: 点击 [0,1075][1080,1202]")
123
+ # ✅ 使用bounds坐标(已验证)
124
+ await client.click("[0,1075][1080,1202]", ref="[0,1075][1080,1202]", verify=False)
125
+ print(f"✅ 点击成功(bounds: [0,1075][1080,1202])")
126
+ await asyncio.sleep(1.5) # 等待页面响应
127
+ # 步骤11: 点击 English
128
+ print(f"\n步骤11: 点击 English")
129
+ # ✅ 使用bounds坐标(已验证)
130
+ await client.click("English", ref="[515,325][565,375]", verify=False)
131
+ print(f"✅ 点击成功(bounds: [515,325][565,375])")
132
+ await asyncio.sleep(1.5) # 等待页面响应
133
+ # 步骤12: 点击 保存
134
+ print(f"\n步骤12: 点击 保存")
135
+ # ✅ 使用text/description定位(已验证)
136
+ await client.click("保存", ref="保存", verify=False)
137
+ print(f"✅ 点击成功(text/desc: 保存)")
138
+ await asyncio.sleep(1.5) # 等待页面响应
139
+ # 步骤13: 点击 重新启动
140
+ print(f"\n步骤13: 点击 重新启动")
141
+ # ✅ 使用text/description定位(已验证)
142
+ await client.click("重新启动", ref="重新启动", verify=False)
143
+ print(f"✅ 点击成功(text/desc: 重新启动)")
144
+ await asyncio.sleep(1.5) # 等待页面响应
145
+
146
+ print("\n✅ 测试完成!")
147
+
148
+ except AssertionError as e:
149
+ print(f"\n❌ 断言失败: {e}")
150
+ # 打印当前页面快照以便调试
151
+ snapshot = await client.snapshot()
152
+ print(f"\n当前页面快照:\n{snapshot[:500]}...")
153
+ raise
154
+ except Exception as e:
155
+ print(f"\n❌ 测试失败: {e}")
156
+ import traceback
157
+ traceback.print_exc()
158
+ raise
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 移动端自动化测试: 堆糖搜索测试
5
+ 生成时间: 2025-11-26 15:39:24
6
+
7
+ 依赖: pip install uiautomator2 pytest pytest-asyncio
8
+
9
+ 运行方式:
10
+ pytest test_测试.py -v -s
11
+ pytest test_测试.py --alluredir=./allure-results # 生成allure报告
12
+ """
13
+
14
+ import time
15
+ import pytest
16
+ import uiautomator2 as u2
17
+
18
+
19
+ PACKAGE_NAME = "com.duitang.main"
20
+ DEVICE_ID = "BEWGF6LFZ5RGS875" # 本地iOS设备 # None表示自动选择第一个设备
21
+
22
+
23
+ @pytest.fixture(scope='function')
24
+ def device():
25
+ """
26
+ pytest fixture: 创建并返回设备连接
27
+ scope='function': 每个测试函数都会创建一个新的连接
28
+ """
29
+ # 连接设备
30
+ d = u2.connect(DEVICE_ID) # None表示自动选择第一个设备
31
+ print(f"\n📱 连接设备: {d.device_info}")
32
+
33
+ # 启动App
34
+ print(f"🚀 启动App: {PACKAGE_NAME}")
35
+ d.app_start(PACKAGE_NAME, stop=True)
36
+ time.sleep(3) # 等待App启动
37
+
38
+ yield d
39
+
40
+ # 清理(可选:关闭App)
41
+ # d.app_stop(PACKAGE_NAME)
42
+
43
+
44
+ def test_堆糖搜索测试(device):
45
+ """
46
+ 测试用例: 堆糖搜索测试
47
+
48
+ 测试步骤:
49
+ 1. 打开com.duitang.main
50
+ 2. 点击底部"我"
51
+ 3. 点击"不同意"
52
+ 4. 点击"首页"
53
+ 5. 搜索框输入"测试"
54
+ 6. 点击"搜索"
55
+ 7. 点击返回
56
+ 8. 点击返回
57
+ 9. 断言回到了首页
58
+
59
+ Args:
60
+ device: pytest fixture,已启动App的设备连接
61
+ """
62
+ d = device
63
+
64
+ # 步骤1: 点击底部"我"
65
+ print(f"\n步骤1: 点击底部'我'")
66
+ d.click(972, 2288) # 使用MCP验证过的坐标
67
+ time.sleep(1.5)
68
+
69
+ # 步骤2: 点击"不同意"
70
+ print(f"\n步骤2: 点击'不同意'")
71
+ d(resourceId="com.duitang.main:id/welcome_policies_disagree").click()
72
+ time.sleep(1.5)
73
+
74
+ # 步骤3: 点击"首页"
75
+ print(f"\n步骤3: 点击'首页'")
76
+ d(resourceId="com.duitang.main:id/ex_tab_title", text="首页").click()
77
+ time.sleep(1.5)
78
+
79
+ # 步骤4: 点击搜索框
80
+ print(f"\n步骤4: 点击搜索框")
81
+ d.click(540, 338) # 使用MCP验证过的坐标
82
+ time.sleep(1.5)
83
+
84
+ # 步骤5: 点击搜索输入框
85
+ print(f"\n步骤5: 点击搜索输入框")
86
+ d(resourceId="com.duitang.main:id/etSearch").click()
87
+ time.sleep(1.5)
88
+
89
+ # 步骤6: 点击最近搜索"测试"
90
+ print(f"\n步骤6: 点击最近搜索'测试'")
91
+ d.click(118, 396) # 使用MCP验证过的坐标
92
+ time.sleep(1.5)
93
+
94
+ # 步骤7: 点击"搜索"
95
+ print(f"\n步骤7: 点击'搜索'")
96
+ d(resourceId="com.duitang.main:id/search_bar_search_btn").click()
97
+ time.sleep(1.5)
98
+
99
+ # 步骤8: 点击返回
100
+ print(f"\n步骤8: 点击返回")
101
+ d.press("back")
102
+ time.sleep(1.5)
103
+
104
+ # 步骤9: 点击返回
105
+ print(f"\n步骤9: 点击返回")
106
+ d.press("back")
107
+ time.sleep(1.5)
108
+
109
+ # 步骤10: 断言回到了首页
110
+ print(f"\n步骤10: 断言回到了首页")
111
+ assert d(text="首页").exists(), "断言失败: 未能回到首页"
112
+
113
+ # ✅ 测试完成
114
+ print("✅ 测试通过")
File without changes
File without changes
File without changes