mobile-mcp-ai 2.6.0__tar.gz → 2.6.1__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.6.0/mobile_mcp_ai.egg-info → mobile_mcp_ai-2.6.1}/PKG-INFO +1 -1
  2. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/basic_tools_lite.py +40 -3
  3. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mcp_tools/mcp_server.py +22 -17
  4. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1/mobile_mcp_ai.egg-info}/PKG-INFO +1 -1
  5. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mobile_mcp_ai.egg-info/SOURCES.txt +10 -0
  6. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/setup.py +1 -1
  7. mobile_mcp_ai-2.6.1/tests/test_mind_cloud_my_space.py +80 -0
  8. mobile_mcp_ai-2.6.1/tests/test_mind_correct.py +73 -0
  9. mobile_mcp_ai-2.6.1/tests/test_mind_improved.py +83 -0
  10. mobile_mcp_ai-2.6.1/tests/test_mind_optimized.py +77 -0
  11. mobile_mcp_ai-2.6.1/tests/test_open_mind.py +37 -0
  12. mobile_mcp_ai-2.6.1/tests/test_priority_demo.py +81 -0
  13. mobile_mcp_ai-2.6.1/tests/test_simple.py +76 -0
  14. mobile_mcp_ai-2.6.1/tests/test_/344/270/276/346/212/245.py +136 -0
  15. mobile_mcp_ai-2.6.1/tests/test_/345/210/207/346/215/242/350/257/255/350/250/200/345/210/260English.py +158 -0
  16. mobile_mcp_ai-2.6.1/tests/test_/346/265/213/350/257/225.py +114 -0
  17. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/LICENSE +0 -0
  18. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/MANIFEST.in +0 -0
  19. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/README.md +0 -0
  20. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/__init__.py +0 -0
  21. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/config.py +0 -0
  22. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/__init__.py +0 -0
  23. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/device_manager.py +0 -0
  24. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/dynamic_config.py +0 -0
  25. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/ios_client_wda.py +0 -0
  26. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/ios_device_manager_wda.py +0 -0
  27. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/mobile_client.py +0 -0
  28. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/template_matcher.py +0 -0
  29. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
  30. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
  31. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
  32. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
  33. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
  34. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
  35. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/utils/__init__.py +0 -0
  36. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/utils/logger.py +0 -0
  37. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/utils/operation_history_manager.py +0 -0
  38. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/core/utils/smart_wait.py +0 -0
  39. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/docs/iOS_SETUP_GUIDE.md +0 -0
  40. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mcp_tools/__init__.py +0 -0
  41. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mobile_mcp_ai.egg-info/dependency_links.txt +0 -0
  42. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mobile_mcp_ai.egg-info/entry_points.txt +0 -0
  43. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mobile_mcp_ai.egg-info/not-zip-safe +0 -0
  44. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mobile_mcp_ai.egg-info/requires.txt +0 -0
  45. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/mobile_mcp_ai.egg-info/top_level.txt +0 -0
  46. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/requirements.txt +0 -0
  47. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/setup.cfg +0 -0
  48. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/templates/close_buttons/auto_x_0112_151217.png +0 -0
  49. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/templates/close_buttons/auto_x_0112_152037.png +0 -0
  50. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/templates/close_buttons/auto_x_0112_152840.png +0 -0
  51. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/templates/close_buttons/auto_x_0112_153256.png +0 -0
  52. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/templates/close_buttons/auto_x_0112_154847.png +0 -0
  53. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/templates/close_buttons/gray_x_stock_ad.png +0 -0
  54. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/utils/__init__.py +0 -0
  55. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/utils/logger.py +0 -0
  56. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/utils/xml_formatter.py +0 -0
  57. {mobile_mcp_ai-2.6.0 → mobile_mcp_ai-2.6.1}/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.6.0
3
+ Version: 2.6.1
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
@@ -2349,6 +2349,7 @@ class BasicMobileToolsLite:
2349
2349
  for elem in root.iter():
2350
2350
  text = elem.attrib.get('text', '')
2351
2351
  content_desc = elem.attrib.get('content-desc', '')
2352
+ resource_id = elem.attrib.get('resource-id', '')
2352
2353
  bounds_str = elem.attrib.get('bounds', '')
2353
2354
  class_name = elem.attrib.get('class', '')
2354
2355
  clickable = elem.attrib.get('clickable', 'false') == 'true'
@@ -2383,6 +2384,13 @@ class BasicMobileToolsLite:
2383
2384
  score = 90
2384
2385
  reason = f"描述='{content_desc}'"
2385
2386
 
2387
+ # 策略2.5:resource-id 包含关闭关键词(如 close_icon, ad_close 等)
2388
+ elif resource_id and any(kw in resource_id.lower() for kw in ['close', 'dismiss', 'skip', 'cancel']):
2389
+ score = 95
2390
+ # 提取简短的 id 名
2391
+ short_id = resource_id.split('/')[-1] if '/' in resource_id else resource_id
2392
+ reason = f"resource-id='{short_id}'"
2393
+
2386
2394
  # 策略3:小尺寸的 clickable 元素(可能是 X 图标)
2387
2395
  elif clickable:
2388
2396
  min_size = max(20, int(screen_width * 0.03))
@@ -2417,7 +2425,9 @@ class BasicMobileToolsLite:
2417
2425
  'center_y': center_y,
2418
2426
  'x_percent': x_percent,
2419
2427
  'y_percent': y_percent,
2420
- 'size': f"{width}x{height}"
2428
+ 'size': f"{width}x{height}",
2429
+ 'resource_id': resource_id,
2430
+ 'text': text
2421
2431
  })
2422
2432
 
2423
2433
  if not candidates:
@@ -2443,7 +2453,16 @@ class BasicMobileToolsLite:
2443
2453
  candidates.sort(key=lambda x: x['score'], reverse=True)
2444
2454
  best = candidates[0]
2445
2455
 
2446
- return {
2456
+ # 生成推荐的点击命令(优先使用 resource-id)
2457
+ if best.get('resource_id'):
2458
+ short_id = best['resource_id'].split('/')[-1] if '/' in best['resource_id'] else best['resource_id']
2459
+ click_cmd = f"mobile_click_by_id('{best['resource_id']}')"
2460
+ elif best.get('text') and best['text'] in ['×', 'X', 'x', '关闭', '取消', '跳过', '知道了']:
2461
+ click_cmd = f"mobile_click_by_text('{best['text']}')"
2462
+ else:
2463
+ click_cmd = f"mobile_click_by_percent({best['x_percent']}, {best['y_percent']})"
2464
+
2465
+ result = {
2447
2466
  "success": True,
2448
2467
  "message": f"✅ 找到可能的关闭按钮",
2449
2468
  "best_candidate": {
@@ -2454,7 +2473,7 @@ class BasicMobileToolsLite:
2454
2473
  "size": best['size'],
2455
2474
  "score": best['score']
2456
2475
  },
2457
- "click_command": f"mobile_click_by_percent({best['x_percent']}, {best['y_percent']})",
2476
+ "click_command": click_cmd,
2458
2477
  "other_candidates": [
2459
2478
  {"reason": c['reason'], "percent": f"({c['x_percent']}%, {c['y_percent']}%)", "score": c['score']}
2460
2479
  for c in candidates[1:4]
@@ -2462,6 +2481,14 @@ class BasicMobileToolsLite:
2462
2481
  "screen_size": {"width": screen_width, "height": screen_height}
2463
2482
  }
2464
2483
 
2484
+ # 如果有 resource-id,额外提供
2485
+ if best.get('resource_id'):
2486
+ result["best_candidate"]["resource_id"] = best['resource_id']
2487
+ if best.get('text'):
2488
+ result["best_candidate"]["text"] = best['text']
2489
+
2490
+ return result
2491
+
2465
2492
  except Exception as e:
2466
2493
  return {"success": False, "message": f"❌ 查找关闭按钮失败: {e}"}
2467
2494
 
@@ -3442,6 +3469,16 @@ class BasicMobileToolsLite:
3442
3469
  reason = f"文本含'{kw}'"
3443
3470
  break
3444
3471
 
3472
+ # resource-id 匹配(如 close_icon, ad_close 等)
3473
+ if resource_id:
3474
+ res_id_lower = resource_id.lower()
3475
+ for kw in ['close', 'dismiss', 'skip', 'cancel']:
3476
+ if kw in res_id_lower:
3477
+ score += 9
3478
+ short_id = resource_id.split('/')[-1] if '/' in resource_id else resource_id
3479
+ reason = f"resource-id='{short_id}'"
3480
+ break
3481
+
3445
3482
  # content-desc 匹配
3446
3483
  for kw in close_content_desc:
3447
3484
  if kw.lower() in content_desc.lower():
@@ -610,22 +610,25 @@ class MobileMCPServer:
610
610
  name="mobile_find_close_button",
611
611
  description="""🔍 智能查找关闭按钮(只找不点,返回位置)
612
612
 
613
- 从元素树中找最可能的关闭按钮,返回坐标和百分比位置。
613
+ ⚡ 【推荐首选】遇到弹窗时优先调用此工具!无需先截图。
614
+
615
+ 从元素树中找最可能的关闭按钮,返回坐标和推荐的点击命令。
614
616
 
615
617
  🎯 识别策略(优先级):
616
- 1. 文本匹配:×、X、关闭、取消、跳过
617
- 2. 描述匹配:content-desc 包含 close/关闭
618
- 3. 小尺寸 clickable 元素(右上角优先)
618
+ 1. 文本匹配:×、X、关闭、取消、跳过 等(得分100)
619
+ 2. resource-id 匹配:包含 close/dismiss/skip(得分95)
620
+ 3. content-desc 匹配:包含 close/关闭(得分90)
621
+ 4. 小尺寸 clickable 元素(右上角优先,得分70+)
619
622
 
620
623
  ✅ 返回内容:
621
624
  - 坐标 (x, y) 和百分比 (x%, y%)
622
- - 推荐的点击命令:mobile_click_by_percent(x%, y%)
623
- - 多个候选位置(供确认)
625
+ - resource-id(如果有)
626
+ - 推荐的点击命令(优先 click_by_id,其次 click_by_text,最后 click_by_percent)
624
627
 
625
628
  💡 使用流程:
626
- 1. 调用此工具找到关闭按钮位置
627
- 2. 确认位置正确后,用 mobile_click_by_percent 点击
628
- 3. 百分比点击兼容不同分辨率手机""",
629
+ 1. 直接调用此工具(无需先截图/列元素)
630
+ 2. 根据返回的 click_command 执行点击
631
+ 3. 如果返回 success=false,才需要截图分析""",
629
632
  inputSchema={"type": "object", "properties": {}, "required": []}
630
633
  ))
631
634
 
@@ -711,24 +714,26 @@ class MobileMCPServer:
711
714
  name="mobile_close_ad",
712
715
  description="""🚫 【推荐】智能关闭广告弹窗
713
716
 
714
- 专门用于关闭广告弹窗,按优先级自动尝试多种方式:
717
+ ⚡ 直接调用即可,无需先截图!会自动按优先级尝试:
715
718
 
716
- 1️⃣ **控件树查找**(最可靠)
717
- - 自动查找"关闭"、"跳过"、"×"等关闭按钮
719
+ 1️⃣ **控件树查找**(最可靠,优先)
720
+ - 自动查找 resource-id 包含 close/dismiss
721
+ - 查找文本"关闭"、"跳过"、"×"等
718
722
  - 找到直接点击,实时可靠
719
723
 
720
724
  2️⃣ **模板匹配**(次优)
721
725
  - 用 OpenCV 匹配已保存的 X 按钮模板
722
- - 需要积累模板库,模板越多成功率越高
726
+ - 模板越多成功率越高
723
727
 
724
728
  3️⃣ **返回截图供 AI 分析**(兜底)
725
- - 如果前两步失败,返回截图
729
+ - 前两步都失败才截图
726
730
  - AI 分析后用 mobile_click_by_percent 点击
727
- - 点击成功后用 mobile_template_add 添加模板(自动学习)
731
+ - 点击成功后用 mobile_template_add 添加模板
728
732
 
729
- 💡 使用流程:
730
- 1. 遇到广告弹窗 → 调用此工具
733
+ 💡 正确流程:
734
+ 1. 遇到广告弹窗 → 直接调用此工具
731
735
  2. 如果成功 → 完成
736
+ 3. 只有失败时才需要截图分析
732
737
  3. 如果失败 → 看截图找 X → 点击 → 添加模板""",
733
738
  inputSchema={
734
739
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobile-mcp-ai
3
- Version: 2.6.0
3
+ Version: 2.6.1
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
@@ -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.6.0", # list_elements 增加文本过滤,保留有文本的元素
28
+ version="2.6.1", # find_close_button 增加 resource-id 匹配 + list_elements 文本过滤
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
File without changes
File without changes
File without changes