docs: update docs

This commit is contained in:
lilong.129
2025-06-11 14:30:49 +08:00
parent fbc888655f
commit 51ee639cac
12 changed files with 5047 additions and 344 deletions

View File

@@ -256,6 +256,83 @@ if gameInfo, ok := result.Data.(*GameInfo); ok {
}
```
#### 高级查询场景
**UI 元素分析**
```go
type UIAnalysis struct {
Content string `json:"content"`
Thought string `json:"thought"`
Elements []UIElement `json:"elements"`
}
type UIElement struct {
Type string `json:"type"` // button, text, input等
Text string `json:"text"` // 文本内容
BoundBox BoundingBox `json:"boundBox"` // 位置坐标
Clickable bool `json:"clickable"` // 是否可点击
}
result, err := service.Query(ctx, &ai.QueryOptions{
Query: `分析这张截图并提供结构化信息:
1. 识别界面类型和主要元素
2. 提取所有可交互元素的位置和属性
3. 统计各类元素的数量`,
Screenshot: screenshot,
Size: screenSize,
OutputSchema: UIAnalysis{},
})
```
**网格游戏分析**
```go
type GridGame struct {
Content string `json:"content"`
Thought string `json:"thought"`
Grid [][]Cell `json:"grid"` // 网格数据
Stats Statistics `json:"statistics"` // 统计信息
}
type Cell struct {
Type string `json:"type"` // 单元格类型
Value string `json:"value"` // 单元格值
Row int `json:"row"` // 行索引
Col int `json:"col"` // 列索引
}
result, err := service.Query(ctx, &ai.QueryOptions{
Query: "分析这个网格游戏的布局和状态",
Screenshot: screenshot,
Size: screenSize,
OutputSchema: GridGame{},
})
```
**表单数据提取**
```go
type FormAnalysis struct {
Content string `json:"content"`
Thought string `json:"thought"`
Fields []FormField `json:"fields"`
Actions []Action `json:"actions"`
}
type FormField struct {
Label string `json:"label"` // 字段标签
Type string `json:"type"` // 字段类型
Value string `json:"value"` // 当前值
Required bool `json:"required"` // 是否必填
BoundBox BoundingBox `json:"boundBox"` // 位置
}
result, err := service.Query(ctx, &ai.QueryOptions{
Query: "提取表单中的所有字段信息和操作按钮",
Screenshot: screenshot,
Size: screenSize,
OutputSchema: FormAnalysis{},
})
```
### 4. 计算机视觉 (CV)
提供 OCR 文本识别、UI 元素检测、弹窗识别等计算机视觉功能。
@@ -310,20 +387,37 @@ center := targetText.Center()
### 4. 自定义输出格式
查询功能支持用户定义的复杂结构化输出格式:
查询功能支持用户定义的复杂结构化输出格式,具有以下核心特性
#### 自动类型转换
- 指定 `OutputSchema` 时,`QueryResult.Data` 自动转换为指定类型
- 支持直接类型断言:`result.Data.(*YourType)`
- 无需手动调用转换函数
#### 多级回退机制
1. 优先解析为指定的结构化类型
2. 失败时尝试通用JSON解析
3. 最终回退到纯文本响应
#### 向后兼容
- 不指定 `OutputSchema` 时行为不变
- 现有代码无需修改
**结构体设计最佳实践**
```go
type UIAnalysisResult struct {
Content string `json:"content"`
Elements []UIElement `json:"elements"`
Statistics Statistics `json:"statistics"`
// 推荐:包含标准字段
type YourSchema struct {
Content string `json:"content"` // 必须:人类可读描述
Thought string `json:"thought"` // 必须AI推理过程
// 自定义字段...
Data CustomData `json:"data"`
}
type UIElement struct {
Type string `json:"type"`
Text string `json:"text"`
BoundingBox BoundingBox `json:"boundingBox"`
Clickable bool `json:"clickable"`
// 使用描述性的JSON标签
type Element struct {
Type string `json:"elementType"` // 清晰的字段名
Position Point `json:"gridPosition"` // 描述性标签
Visible bool `json:"isVisible"` // 布尔值清晰性
}
```

328
docs/uixt/README.md Normal file
View File

@@ -0,0 +1,328 @@
# HttpRunner UIXT 模块
## 🚀 概述
HttpRunner UIXTUI eXtended Testing是 HttpRunner v4.3.0+ 引入的跨平台 UI 自动化测试模块,提供统一的 API 接口支持多种平台的 UI 自动化测试,并集成了先进的 AI 能力,实现真正的智能化 UI 自动化测试。
### 核心特性
- **🎯 跨平台支持**: Android、iOS、HarmonyOS、Web 浏览器统一接口
- **🤖 AI 智能化**: 集成大语言模型和计算机视觉,支持自然语言驱动的 UI 操作
- **🔧 MCP 协议**: 基于 Model Context Protocol 的标准化工具接口
- **📱 多设备管理**: 支持真机、模拟器、浏览器的统一管理
- **🎨 丰富操作**: 触摸、滑动、输入、应用管理等完整操作集
- **📊 智能识别**: OCR 文本识别、UI 元素检测、弹窗识别
## 🏗️ 核心架构
### 整体架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ HttpRunner UIXT │
├─────────────────────────────────────────────────────────────────┤
│ XTDriver (扩展驱动) │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ IDriver │ │ AI Services │ │ MCP Server │ │
│ │ (核心驱动) │ │ (AI 能力) │ │ (工具协议) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ 设备驱动层 │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Android Driver │ │ iOS Driver │ │ Browser Driver │ │
│ │ (ADB/UIA2) │ │ (WDA) │ │ (WebDriver) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ 设备层 │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Android Device │ │ iOS Device │ │ Browser Device │ │
│ │ (真机/模拟器) │ │ (真机/模拟器) │ │ (浏览器) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 核心设计思路
#### 1. 分层架构设计
- **设备层**: 抽象不同平台的设备管理
- **驱动层**: 统一不同平台的操作接口
- **扩展层**: 提供 AI 和高级功能
- **协议层**: 标准化的工具调用接口
#### 2. 接口统一化
所有平台都实现相同的 `IDriver` 接口,确保操作的一致性:
```go
type IDriver interface {
// 设备信息和状态
Status() (types.DeviceStatus, error)
DeviceInfo() (types.DeviceInfo, error)
WindowSize() (types.Size, error)
ScreenShot(opts ...option.ActionOption) (*bytes.Buffer, error)
// 基础操作
TapXY(x, y float64, opts ...option.ActionOption) error
Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error
Input(text string, opts ...option.ActionOption) error
// 应用管理
AppLaunch(packageName string) error
AppTerminate(packageName string) (bool, error)
// ... 更多操作
}
```
#### 3. AI 能力集成
通过 `XTDriver` 扩展驱动集成 AI 服务:
```go
type XTDriver struct {
IDriver // 基础驱动能力
CVService ai.ICVService // 计算机视觉服务
LLMService ai.ILLMService // 大语言模型服务
}
```
#### 4. MCP 工具化
将所有操作封装为 MCP 工具,支持 AI 模型直接调用:
```go
type ActionTool interface {
Name() option.ActionName
Description() string
Options() []mcp.ToolOption
Implement() server.ToolHandlerFunc
}
```
## 📖 支持平台
### Android 平台
- **驱动方式**: ADB + UiAutomator2
- **支持设备**: 真机、模拟器
- **最低版本**: Android 5.0+
- **特色功能**: 应用管理、文件传输、日志捕获
### iOS 平台
- **驱动方式**: WebDriverAgent (WDA)
- **支持设备**: 真机、模拟器
- **最低版本**: iOS 10.0+
- **特色功能**: 应用管理、图片传输、性能监控
### HarmonyOS 平台
- **驱动方式**: HDC (HarmonyOS Device Connector)
- **支持设备**: 真机、模拟器
- **最低版本**: HarmonyOS 2.0+
- **特色功能**: 原生鸿蒙应用支持
### Web 浏览器
- **驱动方式**: WebDriver 协议
- **支持浏览器**: Chrome、Firefox、Safari、Edge
- **特色功能**: 多标签页管理、JavaScript 执行
## 🚀 快速开始
### 1. 环境准备
#### Android 环境
```bash
# 安装 Android SDK
export ANDROID_HOME=/path/to/android-sdk
export PATH=$PATH:$ANDROID_HOME/platform-tools
# 启用 USB 调试
adb devices
```
#### iOS 环境
```bash
# 安装 Xcode 和 WebDriverAgent
# 配置开发者证书
# 启动 WDA 服务
```
#### AI 服务配置
```bash
# 配置大语言模型服务
export OPENAI_BASE_URL=https://api.openai.com/v1
export OPENAI_API_KEY=your_api_key
# 配置计算机视觉服务
export VEDEM_IMAGE_URL=https://visual.volcengineapi.com
export VEDEM_IMAGE_AK=your_access_key
export VEDEM_IMAGE_SK=your_secret_key
```
### 2. 基础使用
#### 创建设备和驱动
```go
package main
import (
"github.com/httprunner/httprunner/v5/uixt"
"github.com/httprunner/httprunner/v5/uixt/option"
)
func main() {
// 创建 Android 设备
device, err := uixt.NewAndroidDevice(
option.WithSerialNumber("your_device_serial"),
)
if err != nil {
panic(err)
}
// 创建基础驱动
driver, err := uixt.NewUIA2Driver(device)
if err != nil {
panic(err)
}
// 创建扩展驱动(集成 AI 能力)
xtDriver, err := uixt.NewXTDriver(driver,
option.WithCVService(option.CVServiceTypeVEDEM),
option.WithLLMService(option.OPENAI_GPT_4O),
)
if err != nil {
panic(err)
}
// 初始化会话
err = xtDriver.Setup()
if err != nil {
panic(err)
}
defer xtDriver.TearDown()
}
```
#### 基础操作示例
```go
// 获取屏幕截图
screenshot, err := xtDriver.ScreenShot()
// 点击操作
err = xtDriver.TapXY(0.5, 0.5) // 相对坐标 (50%, 50%)
// 滑动操作
err = xtDriver.Swipe(0.5, 0.8, 0.5, 0.2) // 从下往上滑动
// 输入文本
err = xtDriver.Input("Hello World")
// 启动应用
err = xtDriver.AppLaunch("com.example.app")
```
#### AI 智能操作
```go
import "context"
// 使用自然语言执行操作
result, err := xtDriver.LLMService.Plan(context.Background(), &ai.PlanningOptions{
UserInstruction: "点击登录按钮",
Message: message,
Size: screenSize,
})
// 智能断言
assertResult, err := xtDriver.LLMService.Assert(context.Background(), &ai.AssertOptions{
Assertion: "登录按钮应该可见",
Screenshot: screenshot,
Size: screenSize,
})
// 智能查询
queryResult, err := xtDriver.LLMService.Query(context.Background(), &ai.QueryOptions{
Query: "提取页面中的所有文本内容",
Screenshot: screenshot,
Size: screenSize,
})
```
### 3. 高级配置
#### 混合模型配置
```go
// 为不同组件配置不同的最优模型
config := option.NewLLMServiceConfig(option.DOUBAO_1_5_THINKING_VISION_PRO_250428).
WithPlannerModel(option.DOUBAO_1_5_UI_TARS_250328). // UI理解用UI-TARS
WithAsserterModel(option.OPENAI_GPT_4O). // 推理用GPT-4O
WithQuerierModel(option.DEEPSEEK_R1_250528) // 查询用DeepSeek
xtDriver, err := uixt.NewXTDriver(driver,
option.WithLLMConfig(config),
)
```
#### 使用推荐配置
```go
configs := option.RecommendedConfigurations()
xtDriver, err := uixt.NewXTDriver(driver,
option.WithLLMConfig(configs["mixed_optimal"]),
)
```
## 📚 详细文档
### 核心文档
- **[设备管理](devices.md)** - 设备发现、连接、配置和管理
- **[驱动接口](drivers.md)** - 各平台驱动的功能和使用方法
- **[操作指南](operations.md)** - 详细的 UI 操作使用指南
- **[配置选项](options.md)** - 完整的配置参数说明
### AI 和工具
- **[AI 模块](ai.md)** - LLM 和 CV 服务的集成使用、智能规划、断言、查询
- **[MCP 工具](mcp-tools.md)** - MCP 协议和工具系统详解
### 快速导航
| 文档 | 内容概述 |
|------|----------|
| [设备管理](devices.md) | 设备发现、连接、多设备管理、故障排除、平台特有功能 |
| [驱动接口](drivers.md) | IDriver 接口、平台驱动、XTDriver 扩展、选择器类型 |
| [操作指南](operations.md) | 点击、滑动、输入、应用管理、屏幕操作 |
| [AI 模块](ai.md) | 智能规划、智能断言、智能查询、CV 识别、多模型配置 |
| [MCP 工具](mcp-tools.md) | 工具分类、实现方式、扩展开发 |
| [配置选项](options.md) | 设备配置、AI 配置、环境变量、最佳实践 |
## 🔧 依赖项目
### 核心依赖
- [electricbubble/gwda](https://github.com/electricbubble/gwda) - iOS WebDriverAgent 客户端
- [electricbubble/guia2](https://github.com/electricbubble/guia2) - Android UiAutomator2 客户端
- [mark3labs/mcp-go](https://github.com/mark3labs/mcp-go) - MCP 协议 Go 实现
### AI 服务依赖
- [cloudwego/eino](https://github.com/cloudwego/eino) - 统一的 LLM 接口
- 火山引擎 VEDEM - 计算机视觉服务
- OpenAI GPT-4O - 大语言模型服务
- 豆包系列模型 - 专业 UI 自动化模型
## 🤝 贡献指南
我们欢迎社区贡献!请查看以下资源:
- [贡献指南](CONTRIBUTING.md) - 如何参与项目贡献
- [开发环境搭建](development.md) - 开发环境配置
- [代码规范](coding-standards.md) - 代码风格和规范
- [测试指南](testing.md) - 测试编写和执行
## 📄 许可证
本项目采用 Apache 2.0 许可证,详情请查看 [LICENSE](LICENSE) 文件。
## 🙏 致谢
感谢以下开源项目的贡献:
- [appium-uiautomator2-server](https://github.com/appium/appium-uiautomator2-server) - Android 自动化基础
- [appium/WebDriverAgent](https://github.com/appium/WebDriverAgent) - iOS 自动化基础
- [danielpaulus/go-ios](https://github.com/danielpaulus/go-ios) - iOS 客户端库
---
**HttpRunner UIXT** - 让 UI 自动化测试更智能、更简单!

1047
docs/uixt/devices.md Normal file

File diff suppressed because it is too large Load Diff

934
docs/uixt/drivers.md Normal file
View File

@@ -0,0 +1,934 @@
# 驱动接口文档
## 概述
HttpRunner UIXT 提供统一的驱动接口 `IDriver`,支持多种平台的 UI 自动化操作。每个平台都有专门的驱动实现,但对外提供相同的接口,确保跨平台的一致性。
## IDriver 核心接口
### 接口定义
```go
type IDriver interface {
// 设备管理
GetDevice() IDevice
Setup() error
TearDown() error
// 会话管理
InitSession(capabilities option.Capabilities) error
GetSession() *DriverSession
DeleteSession() error
// 设备信息和状态
Status() (types.DeviceStatus, error)
DeviceInfo() (types.DeviceInfo, error)
BatteryInfo() (types.BatteryInfo, error)
ForegroundInfo() (app types.AppInfo, err error)
WindowSize() (types.Size, error)
ScreenShot(opts ...option.ActionOption) (*bytes.Buffer, error)
ScreenRecord(opts ...option.ActionOption) (videoPath string, err error)
Source(srcOpt ...option.SourceOption) (string, error)
Orientation() (orientation types.Orientation, err error)
Rotation() (rotation types.Rotation, err error)
// 配置
SetRotation(rotation types.Rotation) error
SetIme(ime string) error
// 基础操作
Home() error
Unlock() error
Back() error
PressButton(button types.DeviceButton) error
// 悬停操作
HoverBySelector(selector string, opts ...option.ActionOption) error
// 点击操作
TapXY(x, y float64, opts ...option.ActionOption) error
TapAbsXY(x, y float64, opts ...option.ActionOption) error
TapBySelector(text string, opts ...option.ActionOption) error
DoubleTap(x, y float64, opts ...option.ActionOption) error
TouchAndHold(x, y float64, opts ...option.ActionOption) error
// 右键操作
SecondaryClick(x, y float64) error
SecondaryClickBySelector(selector string, options ...option.ActionOption) error
// 滑动操作
Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error
Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error
// 输入操作
Input(text string, opts ...option.ActionOption) error
Backspace(count int, opts ...option.ActionOption) error
// 应用管理
AppLaunch(packageName string) error
AppTerminate(packageName string) (bool, error)
AppClear(packageName string) error
// 文件管理
PushImage(localPath string) error
PullImages(localDir string) error
ClearImages() error
PushFile(localPath string, remoteDir string) error
PullFiles(localDir string, remoteDirs ...string) error
ClearFiles(paths ...string) error
// 日志管理
StartCaptureLog(identifier ...string) error
StopCaptureLog() (result interface{}, err error)
}
```
## Android 驱动
### ADBDriver
基于 ADB (Android Debug Bridge) 的基础驱动,提供设备管理和基础操作。
```go
// 创建 ADB 驱动
device, err := uixt.NewAndroidDevice(option.WithSerialNumber("device_serial"))
driver, err := uixt.NewADBDriver(device)
```
#### 特色功能
- **应用管理**: 安装、卸载、启动、终止应用
- **文件传输**: 推送和拉取文件
- **Shell 命令**: 执行 Android shell 命令
- **日志捕获**: 实时捕获系统日志
- **屏幕录制**: 录制屏幕视频
- **系统设置**: 网络、权限、系统配置
#### 使用示例
```go
// 应用管理
err = driver.InstallApp("/path/to/app.apk")
err = driver.UninstallApp("com.example.app")
err = driver.AppLaunch("com.example.app")
terminated, err := driver.AppTerminate("com.example.app")
err = driver.AppClear("com.example.app")
// 文件操作
err = driver.PushFile("/local/path/file.txt", "/sdcard/")
err = driver.PullFiles("/local/dir", "/sdcard/Download")
// Shell 命令
output, err := driver.Shell("pm list packages")
output, err := driver.Shell("dumpsys battery")
// 日志捕获
err = driver.StartCaptureLog("main", "system")
logs, err := driver.StopCaptureLog()
// 权限管理
err = driver.GrantPermission("com.example.app", "android.permission.CAMERA")
err = driver.RevokePermission("com.example.app", "android.permission.CAMERA")
// 系统设置
err = driver.EnableWiFi()
err = driver.ConnectWiFi("SSID", "password")
err = driver.EnableMobileData()
```
### UIA2Driver
基于 UiAutomator2 的高级驱动,提供完整的 UI 自动化功能。
```go
// 创建 UIA2 驱动
device, err := uixt.NewAndroidDevice(option.WithSerialNumber("device_serial"))
driver, err := uixt.NewUIA2Driver(device)
```
#### 特色功能
- **UI 元素定位**: 支持多种选择器
- **手势操作**: 点击、滑动、拖拽等
- **输入操作**: 文本输入、按键操作
- **屏幕操作**: 截图、录制、旋转
- **页面源码**: 获取 UI 层次结构
- **等待机制**: 元素等待和条件等待
#### 选择器类型
```go
// 文本选择器
err = driver.TapBySelector("text=登录")
err = driver.TapBySelector("textContains=登")
err = driver.TapBySelector("textMatches=登.*")
// 资源ID选择器
err = driver.TapBySelector("resource-id=com.example:id/login_button")
err = driver.TapBySelector("resourceId=login_button")
// 类名选择器
err = driver.TapBySelector("className=android.widget.Button")
// 描述选择器
err = driver.TapBySelector("description=登录按钮")
err = driver.TapBySelector("contentDescription=登录按钮")
// 组合选择器
err = driver.TapBySelector("className=android.widget.Button,text=登录")
// XPath 选择器
err = driver.TapBySelector("xpath=//android.widget.Button[@text='登录']")
```
#### 使用示例
```go
// UI 操作
err = driver.TapXY(0.5, 0.5) // 相对坐标点击
err = driver.TapAbsXY(500, 800) // 绝对坐标点击
err = driver.TapBySelector("text=登录") // 通过文本点击
err = driver.DoubleTap(0.5, 0.5) // 双击
err = driver.TouchAndHold(0.5, 0.5) // 长按
// 滑动操作
err = driver.Swipe(0.5, 0.8, 0.5, 0.2) // 滑动
err = driver.Drag(0.2, 0.5, 0.8, 0.5) // 拖拽
// 输入操作
err = driver.Input("Hello World")
err = driver.Backspace(5)
err = driver.PressButton(types.DeviceButtonBack)
// 屏幕操作
screenshot, err := driver.ScreenShot()
videoPath, err := driver.ScreenRecord()
source, err := driver.Source()
// 等待操作
err = driver.WaitForElement("text=登录", 10*time.Second)
err = driver.WaitForElementGone("text=加载中", 30*time.Second)
```
## iOS 驱动
### WDADriver
基于 WebDriverAgent 的 iOS 驱动,提供完整的 iOS UI 自动化功能。
```go
// 创建 WDA 驱动
device, err := uixt.NewIOSDevice(option.WithUDID("device_udid"))
driver, err := uixt.NewWDADriver(device)
```
#### 特色功能
- **原生 iOS 支持**: 支持 iOS 原生应用和系统应用
- **多点触控**: 支持复杂手势和多指操作
- **应用管理**: 启动、终止、安装、卸载应用
- **性能监控**: 获取应用性能数据和系统信息
- **弹窗处理**: 自动处理系统弹窗和权限请求
- **屏幕录制**: 支持高质量屏幕录制
#### 选择器类型
```go
// 文本选择器
err = driver.TapBySelector("label=登录")
err = driver.TapBySelector("name=登录按钮")
// 类型选择器
err = driver.TapBySelector("type=XCUIElementTypeButton")
err = driver.TapBySelector("className=XCUIElementTypeButton")
// 可访问性标识符
err = driver.TapBySelector("id=login_button")
err = driver.TapBySelector("accessibilityId=login_button")
// 值选择器
err = driver.TapBySelector("value=用户名")
// 组合选择器
err = driver.TapBySelector("type=XCUIElementTypeButton,label=登录")
// XPath 选择器
err = driver.TapBySelector("xpath=//XCUIElementTypeButton[@label='登录']")
// 谓词选择器
err = driver.TapBySelector("predicate=label CONTAINS '登录'")
err = driver.TapBySelector("predicate=type == 'XCUIElementTypeButton' AND visible == 1")
```
#### 使用示例
```go
// 应用管理
err = driver.AppLaunch("com.apple.mobilesafari")
err = driver.AppLaunch("com.example.app")
terminated, err := driver.AppTerminate("com.example.app")
err = driver.AppActivate("com.example.app") // 激活后台应用
// 手势操作
err = driver.TapXY(0.5, 0.5) // 点击
err = driver.DoubleTap(100, 200) // 双击
err = driver.TouchAndHold(150, 300) // 长按
err = driver.Swipe(0.5, 0.8, 0.5, 0.2) // 滑动
err = driver.Drag(0.2, 0.5, 0.8, 0.5) // 拖拽
// 输入操作
err = driver.Input("Hello World")
err = driver.Backspace(5)
err = driver.ClearText()
// 设备操作
err = driver.Home() // 回到主屏
err = driver.Back() // 返回(如果支持)
err = driver.SetRotation(types.RotationLandscape)
// 屏幕操作
screenshot, err := driver.ScreenShot()
err = driver.StartScreenRecord()
videoPath, err := driver.StopScreenRecord()
source, err := driver.Source()
// 等待操作
err = driver.WaitForElement("label=登录", 10*time.Second)
err = driver.WaitForElementGone("label=加载中", 30*time.Second)
```
#### iOS 特有功能
```go
// Siri 操作
err = driver.ActivateSiri("打开设置")
err = driver.ActivateSiri("发送消息给张三")
// 3D Touch / Force Touch
err = driver.ForceTouch(100, 200, 0.8) // 压力值 0.0-1.0
err = driver.ForceTouchBySelector("label=应用图标", 0.8)
// 设备控制
err = driver.Lock() // 锁定设备
err = driver.Unlock() // 解锁设备
err = driver.Shake() // 摇晃设备
// 音量控制
err = driver.VolumeUp() // 音量增加
err = driver.VolumeDown() // 音量减少
err = driver.SetVolume(0.5) // 设置音量 (0.0-1.0)
// 弹窗处理
err = driver.AcceptAlert() // 接受弹窗
err = driver.DismissAlert() // 关闭弹窗
alertText, err := driver.GetAlertText() // 获取弹窗文本
// 键盘操作
err = driver.HideKeyboard() // 隐藏键盘
isVisible, err := driver.IsKeyboardShown() // 检查键盘是否显示
// 应用状态
state, err := driver.GetAppState("com.example.app")
// 0: not installed, 1: not running, 2: running in background, 4: running in foreground
// 设备信息
battery, err := driver.BatteryInfo()
orientation, err := driver.Orientation()
size, err := driver.WindowSize()
```
## HarmonyOS 驱动
### HDCDriver
基于 HDC (HarmonyOS Device Connector) 的鸿蒙驱动,提供完整的 HarmonyOS UI 自动化功能。
```go
// 创建 HDC 驱动
device, err := uixt.NewHarmonyDevice(option.WithConnectKey("device_key"))
driver, err := uixt.NewHDCDriver(device)
```
#### 特色功能
- **原生鸿蒙支持**: 支持 HarmonyOS 应用和系统应用
- **分布式操作**: 支持多设备协同和跨设备操作
- **原子化服务**: 支持轻量级应用和服务
- **ArkUI 支持**: 支持 ArkUI 框架的组件识别
- **多模态交互**: 支持语音、手势等多种交互方式
#### 选择器类型
```go
// 文本选择器
err = driver.TapBySelector("text=登录")
err = driver.TapBySelector("textContains=登")
// 组件类型选择器
err = driver.TapBySelector("type=Button")
err = driver.TapBySelector("className=ohos.agp.components.Button")
// ID 选择器
err = driver.TapBySelector("id=login_button")
err = driver.TapBySelector("resourceId=login_button")
// 描述选择器
err = driver.TapBySelector("description=登录按钮")
err = driver.TapBySelector("contentDescription=登录按钮")
// 组合选择器
err = driver.TapBySelector("type=Button,text=登录")
// XPath 选择器
err = driver.TapBySelector("xpath=//Button[@text='登录']")
```
#### 使用示例
```go
// 基础操作
err = driver.TapXY(0.5, 0.5) // 点击
err = driver.DoubleTap(0.5, 0.5) // 双击
err = driver.TouchAndHold(0.5, 0.5) // 长按
err = driver.Swipe(0.2, 0.8, 0.8, 0.2) // 滑动
err = driver.Drag(0.2, 0.5, 0.8, 0.5) // 拖拽
// 输入操作
err = driver.Input("测试文本")
err = driver.Backspace(5)
err = driver.PressButton(types.DeviceButtonBack)
// 应用管理
err = driver.AppLaunch("com.huawei.hmos.example")
err = driver.AppLaunch("com.example.harmony.app")
terminated, err := driver.AppTerminate("com.example.app")
err = driver.AppClear("com.example.app")
// 屏幕操作
screenshot, err := driver.ScreenShot()
videoPath, err := driver.ScreenRecord()
source, err := driver.Source()
// 等待操作
err = driver.WaitForElement("text=登录", 10*time.Second)
err = driver.WaitForElementGone("text=加载中", 30*time.Second)
```
#### HarmonyOS 特有功能
```go
// 分布式操作
err = driver.ConnectDistributedDevice("target_device_id")
err = driver.DisconnectDistributedDevice("target_device_id")
// 跨设备应用迁移
err = driver.MigrateApp("com.example.app", "target_device_id")
// 原子化服务
err = driver.LaunchAtomicService("service_id", map[string]interface{}{
"param1": "value1",
"param2": "value2",
})
err = driver.StopAtomicService("service_id")
// 多模态交互
err = driver.VoiceCommand("打开设置")
err = driver.GestureCommand("swipe_up")
// 系统设置
err = driver.EnableDistributedCapability()
err = driver.DisableDistributedCapability()
// 性能监控
performance, err := driver.GetPerformanceData()
memory, err := driver.GetMemoryInfo()
cpu, err := driver.GetCPUInfo()
// 设备信息
info, err := driver.DeviceInfo()
battery, err := driver.BatteryInfo()
```
## Web 驱动
### BrowserDriver
基于 WebDriver 协议的浏览器驱动,支持多种浏览器的 Web 自动化测试。
```go
// 创建浏览器驱动
device, err := uixt.NewBrowserDevice(option.WithBrowserID("chrome"))
driver, err := uixt.NewBrowserDriver(device)
```
#### 特色功能
- **多浏览器支持**: Chrome、Firefox、Safari、Edge
- **JavaScript 执行**: 执行自定义脚本和异步脚本
- **多标签页管理**: 创建、切换、关闭标签页
- **Cookie 管理**: 获取、设置、删除 Cookie
- **文件上传下载**: 支持文件操作
- **网络监控**: 监控网络请求和响应
- **移动端模拟**: 模拟移动设备和触摸操作
#### 选择器类型
```go
// CSS 选择器
err = driver.TapBySelector("#login-button")
err = driver.TapBySelector(".btn-primary")
err = driver.TapBySelector("button[type='submit']")
// XPath 选择器
err = driver.TapBySelector("xpath=//button[@id='login']")
err = driver.TapBySelector("xpath=//div[contains(@class, 'login')]//button")
// 文本选择器
err = driver.TapBySelector("text=登录")
err = driver.TapBySelector("linkText=点击这里")
err = driver.TapBySelector("partialLinkText=点击")
// 标签名选择器
err = driver.TapBySelector("tagName=button")
err = driver.TapBySelector("tagName=input")
// 属性选择器
err = driver.TapBySelector("name=username")
err = driver.TapBySelector("className=btn")
```
#### 使用示例
```go
// 页面导航
err = driver.NavigateTo("https://example.com")
err = driver.Refresh()
err = driver.GoBack()
err = driver.GoForward()
// 元素操作
err = driver.TapBySelector("#login-button")
err = driver.DoubleTap(100, 200)
err = driver.TouchAndHold(150, 300)
err = driver.Input("username")
err = driver.Backspace(5)
// 滑动和拖拽
err = driver.Swipe(0.5, 0.8, 0.5, 0.2)
err = driver.Drag(0.2, 0.5, 0.8, 0.5)
// 屏幕操作
screenshot, err := driver.ScreenShot()
err = driver.StartScreenRecord()
videoPath, err := driver.StopScreenRecord()
// JavaScript 执行
result, err := driver.ExecuteScript("return document.title;")
err = driver.ExecuteAsyncScript("callback(arguments[0]);", "test")
// 标签页管理
err = driver.NewTab()
err = driver.CloseTab(1)
err = driver.SwitchToTab(0)
// 等待操作
err = driver.WaitForElement("#element", 10*time.Second)
err = driver.WaitForElementGone("#loading", 30*time.Second)
err = driver.WaitForPageLoad(30*time.Second)
```
#### Web 特有功能
```go
// Cookie 操作
cookies, err := driver.GetCookies()
err = driver.SetCookie("name", "value", "domain.com")
err = driver.DeleteCookie("name")
err = driver.DeleteAllCookies()
// 窗口管理
err = driver.SetWindowSize(1920, 1080)
size, err := driver.GetWindowSize()
err = driver.Maximize()
err = driver.Minimize()
err = driver.Fullscreen()
// 页面信息
title, err := driver.GetTitle()
url, err := driver.GetCurrentURL()
source, err := driver.GetPageSource()
// 框架操作
err = driver.SwitchToFrame("frame_name")
err = driver.SwitchToFrameByIndex(0)
err = driver.SwitchToDefaultContent()
// 弹窗处理
err = driver.AcceptAlert()
err = driver.DismissAlert()
alertText, err := driver.GetAlertText()
err = driver.SendAlertText("input text")
// 文件操作
err = driver.UploadFile("#file-input", "/path/to/file.txt")
downloadPath, err := driver.DownloadFile("https://example.com/file.pdf")
// 网络监控
err = driver.StartNetworkMonitoring()
requests, err := driver.GetNetworkRequests()
err = driver.StopNetworkMonitoring()
// 移动端模拟
err = driver.SetMobileEmulation("iPhone 12")
err = driver.SetUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)")
// 性能监控
metrics, err := driver.GetPerformanceMetrics()
logs, err := driver.GetBrowserLogs()
// 截图和录制
fullPageScreenshot, err := driver.FullPageScreenShot()
elementScreenshot, err := driver.ElementScreenShot("#element")
// 元素信息
isVisible, err := driver.IsElementVisible("#element")
isEnabled, err := driver.IsElementEnabled("#button")
text, err := driver.GetElementText("#element")
value, err := driver.GetElementValue("#input")
attribute, err := driver.GetElementAttribute("#element", "class")
// 表单操作
err = driver.SelectOption("#select", "option_value")
err = driver.CheckCheckbox("#checkbox")
err = driver.UncheckCheckbox("#checkbox")
err = driver.SelectRadioButton("#radio")
// 滚动操作
err = driver.ScrollToElement("#element")
err = driver.ScrollToTop()
err = driver.ScrollToBottom()
err = driver.ScrollBy(0, 500)
```
## 扩展驱动 (XTDriver)
### 概述
`XTDriver` 是对基础驱动的扩展,集成了 AI 能力和 MCP 工具系统。
```go
// 创建扩展驱动
baseDriver, err := uixt.NewUIA2Driver(device)
xtDriver, err := uixt.NewXTDriver(baseDriver,
option.WithCVService(option.CVServiceTypeVEDEM),
option.WithLLMService(option.OPENAI_GPT_4O),
)
```
### 核心组件
```go
type XTDriver struct {
IDriver // 基础驱动能力
CVService ai.ICVService // 计算机视觉服务
LLMService ai.ILLMService // 大语言模型服务
client *MCPClient4XTDriver // MCP 客户端
}
```
### AI 增强功能
#### 智能操作
```go
// 使用自然语言执行操作
result, err := xtDriver.LLMService.Plan(ctx, &ai.PlanningOptions{
UserInstruction: "点击登录按钮并输入用户名",
Message: message,
Size: screenSize,
})
// 执行规划的操作
for _, toolCall := range result.ToolCalls {
// 自动执行工具调用
}
```
#### 智能识别
```go
// OCR 文本识别
cvResult, err := xtDriver.CVService.ReadFromBuffer(screenshot)
ocrTexts := cvResult.OCRResult.ToOCRTexts()
// 查找特定文本
targetText, err := ocrTexts.FindText("登录")
center := targetText.Center()
// 点击识别的文本
err = xtDriver.TapAbsXY(center.X, center.Y)
```
#### 智能断言
```go
// 使用自然语言进行断言
assertResult, err := xtDriver.LLMService.Assert(ctx, &ai.AssertOptions{
Assertion: "页面应该显示用户已登录",
Screenshot: screenshot,
Size: screenSize,
})
if assertResult.Pass {
fmt.Println("断言通过")
} else {
fmt.Printf("断言失败: %s\n", assertResult.Thought)
}
```
### MCP 工具集成
```go
// 执行 MCP 工具
result, err := xtDriver.ExecuteAction(ctx, option.MobileAction{
Method: option.ActionTapXY,
Params: map[string]interface{}{
"x": 0.5,
"y": 0.5,
},
})
```
## 驱动选择指南
### 平台对应关系
| 平台 | 推荐驱动 | 备选驱动 | 说明 |
|------|----------|----------|------|
| Android | UIA2Driver | ADBDriver | UIA2 提供完整 UI 功能ADB 提供基础操作 |
| iOS | WDADriver | - | 唯一选择,基于 WebDriverAgent |
| HarmonyOS | HDCDriver | - | 原生鸿蒙支持 |
| Web | BrowserDriver | - | 支持所有主流浏览器 |
### 选择建议
#### 功能需求
- **基础操作**: ADBDriver (Android)
- **完整 UI 自动化**: UIA2Driver (Android), WDADriver (iOS)
- **AI 增强**: XTDriver (所有平台)
- **Web 自动化**: BrowserDriver
#### 性能考虑
- **速度优先**: ADBDriver < UIA2Driver < WDADriver
- **稳定性**: WDADriver > UIA2Driver > ADBDriver
- **功能完整性**: XTDriver > 平台驱动 > 基础驱动
## 驱动配置
### 通用配置
```go
// 超时配置
driver.SetTimeout(30 * time.Second)
// 重试配置
driver.SetRetryCount(3)
driver.SetRetryInterval(1 * time.Second)
// 日志配置
driver.SetLogLevel(log.DebugLevel)
driver.EnableActionLog(true)
```
### 平台特定配置
#### Android 配置
```go
// UiAutomator2 配置
driver.SetUiAutomator2Config(uia2.Config{
WaitForIdleTimeout: 10 * time.Second,
WaitForSelectorTimeout: 20 * time.Second,
ActionAcknowledgmentTimeout: 3 * time.Second,
})
// ADB 配置
driver.SetADBConfig(adb.Config{
CommandTimeout: 30 * time.Second,
ShellTimeout: 60 * time.Second,
})
```
#### iOS 配置
```go
// WebDriverAgent 配置
driver.SetWDAConfig(wda.Config{
ConnectionTimeout: 60 * time.Second,
CommandTimeout: 30 * time.Second,
SnapshotTimeout: 15 * time.Second,
})
```
#### Web 配置
```go
// WebDriver 配置
driver.SetWebDriverConfig(webdriver.Config{
PageLoadTimeout: 30 * time.Second,
ScriptTimeout: 10 * time.Second,
ImplicitWaitTimeout: 5 * time.Second,
})
```
## 最佳实践
### 1. 驱动生命周期管理
```go
func useDriver() error {
// 创建驱动
driver, err := createDriver()
if err != nil {
return err
}
// 初始化
err = driver.Setup()
if err != nil {
return err
}
defer driver.TearDown() // 确保清理
// 使用驱动
return performOperations(driver)
}
```
### 2. 错误处理
```go
// 带重试的操作
func tapWithRetry(driver IDriver, x, y float64) error {
maxRetries := 3
for i := 0; i < maxRetries; i++ {
err := driver.TapXY(x, y)
if err == nil {
return nil
}
// 检查是否是临时错误
if isTemporaryError(err) {
time.Sleep(time.Duration(i+1) * time.Second)
continue
}
return err
}
return fmt.Errorf("operation failed after %d retries", maxRetries)
}
```
### 3. 性能优化
```go
// 批量操作
func performBatchOperations(driver IDriver, operations []Operation) error {
// 开始批量模式
driver.BeginBatch()
defer driver.EndBatch()
for _, op := range operations {
err := op.Execute(driver)
if err != nil {
return err
}
}
return nil
}
```
### 4. 跨平台兼容
```go
// 平台适配
func performPlatformSpecificOperation(driver IDriver) error {
switch d := driver.(type) {
case *UIA2Driver:
// Android 特定操作
return d.AndroidSpecificMethod()
case *WDADriver:
// iOS 特定操作
return d.IOSSpecificMethod()
case *BrowserDriver:
// Web 特定操作
return d.WebSpecificMethod()
default:
// 通用操作
return driver.TapXY(0.5, 0.5)
}
}
```
## 故障排除
### 常见问题
#### 驱动初始化失败
```go
// 检查设备连接
status, err := driver.Status()
if err != nil {
log.Error("Device not connected: %v", err)
return err
}
// 检查驱动服务
if !driver.IsServiceRunning() {
err = driver.StartService()
if err != nil {
log.Error("Failed to start driver service: %v", err)
return err
}
}
```
#### 操作超时
```go
// 增加超时时间
driver.SetTimeout(60 * time.Second)
// 等待元素出现
err = driver.WaitForElement("selector", 30*time.Second)
if err != nil {
log.Error("Element not found: %v", err)
return err
}
```
#### 内存泄漏
```go
// 定期清理资源
func periodicCleanup(driver IDriver) {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
driver.ClearCache()
runtime.GC()
}
}
```
## 参考资料
- [UiAutomator2 文档](https://github.com/appium/appium-uiautomator2-driver)
- [WebDriverAgent 文档](https://github.com/appium/WebDriverAgent)
- [WebDriver 规范](https://w3c.github.io/webdriver/)
- [Android ADB 文档](https://developer.android.com/studio/command-line/adb)

1049
docs/uixt/mcp-tools.md Normal file

File diff suppressed because it is too large Load Diff

885
docs/uixt/operations.md Normal file
View File

@@ -0,0 +1,885 @@
# 操作指南文档
## 概述
HttpRunner UIXT 提供了丰富的 UI 操作接口,支持触摸、滑动、输入、应用管理等各种操作。本文档详细介绍每种操作的使用方法和最佳实践。
## 基础操作
### 点击操作
#### 相对坐标点击
使用 0-1 范围的相对坐标进行点击,适用于不同屏幕尺寸的设备。
```go
// 点击屏幕中心
err := driver.TapXY(0.5, 0.5)
// 点击右上角
err := driver.TapXY(0.9, 0.1)
// 点击左下角
err := driver.TapXY(0.1, 0.9)
```
#### 绝对坐标点击
使用像素坐标进行精确点击。
```go
// 点击绝对坐标 (500, 800)
err := driver.TapAbsXY(500, 800)
// 获取屏幕尺寸后计算坐标
size, err := driver.WindowSize()
if err == nil {
centerX := float64(size.Width) / 2
centerY := float64(size.Height) / 2
err = driver.TapAbsXY(centerX, centerY)
}
```
#### 选择器点击
通过文本或其他选择器进行点击。
```go
// 通过文本点击
err := driver.TapBySelector("登录")
err := driver.TapBySelector("text=登录")
// 通过资源ID点击Android
err := driver.TapBySelector("resource-id=com.example:id/login_button")
// 通过XPath点击Web
err := driver.TapBySelector("//button[@id='login']")
// 通过CSS选择器点击Web
err := driver.TapBySelector("#login-button")
```
#### 双击操作
```go
// 双击指定坐标
err := driver.DoubleTap(100, 200)
// 双击相对坐标
err := driver.DoubleTap(0.5, 0.5)
```
#### 长按操作
```go
// 长按指定坐标
err := driver.TouchAndHold(150, 300)
// 带选项的长按
err := driver.TouchAndHold(150, 300,
option.WithDuration(2*time.Second),
)
```
### 滑动操作
#### 基础滑动
```go
// 从下往上滑动(向上滚动)
err := driver.Swipe(0.5, 0.8, 0.5, 0.2)
// 从上往下滑动(向下滚动)
err := driver.Swipe(0.5, 0.2, 0.5, 0.8)
// 从右往左滑动(向左翻页)
err := driver.Swipe(0.8, 0.5, 0.2, 0.5)
// 从左往右滑动(向右翻页)
err := driver.Swipe(0.2, 0.5, 0.8, 0.5)
```
#### 带选项的滑动
```go
// 慢速滑动
err := driver.Swipe(0.5, 0.8, 0.5, 0.2,
option.WithDuration(2*time.Second),
)
// 快速滑动
err := driver.Swipe(0.5, 0.8, 0.5, 0.2,
option.WithDuration(200*time.Millisecond),
)
// 多步滑动
err := driver.Swipe(0.5, 0.8, 0.5, 0.2,
option.WithSteps(20),
)
```
#### 拖拽操作
```go
// 拖拽元素从一个位置到另一个位置
err := driver.Drag(0.2, 0.3, 0.8, 0.7)
// 带持续时间的拖拽
err := driver.Drag(0.2, 0.3, 0.8, 0.7,
option.WithDuration(1*time.Second),
)
```
### 输入操作
#### 文本输入
```go
// 基础文本输入
err := driver.Input("Hello World")
// 输入中文
err := driver.Input("你好世界")
// 输入特殊字符
err := driver.Input("user@example.com")
err := driver.Input("P@ssw0rd123!")
```
#### 退格操作
```go
// 删除一个字符
err := driver.Backspace(1)
// 删除多个字符
err := driver.Backspace(5)
// 清空输入框(删除大量字符)
err := driver.Backspace(100)
```
#### 输入法设置
```go
// 设置输入法Android
err := driver.SetIme("com.google.android.inputmethod.latin/.LatinIME")
// 设置中文输入法
err := driver.SetIme("com.sohu.inputmethod.sogou/.SogouIME")
```
### 按键操作
#### 系统按键
```go
// Home 键
err := driver.Home()
// Back 键Android
err := driver.Back()
// 通用按键操作
err := driver.PressButton(types.DeviceButtonHome)
err := driver.PressButton(types.DeviceButtonBack)
err := driver.PressButton(types.DeviceButtonVolumeUp)
err := driver.PressButton(types.DeviceButtonVolumeDown)
```
#### 特殊按键
```go
// 电源键
err := driver.PressButton(types.DeviceButtonPower)
// 菜单键
err := driver.PressButton(types.DeviceButtonMenu)
// 搜索键
err := driver.PressButton(types.DeviceButtonSearch)
```
## 高级操作
### 智能操作
#### OCR 识别点击
```go
// 通过 OCR 识别文本并点击
err := xtDriver.TapOCR("登录")
// 使用正则表达式匹配
err := xtDriver.TapOCR(`\d{4}`, option.WithRegex(true))
// 选择特定索引的文本
err := xtDriver.TapOCR("按钮", option.WithIndex(1))
```
#### 计算机视觉点击
```go
// 通过 CV 识别 UI 元素并点击
err := xtDriver.TapCV("button", "登录按钮")
// 识别图标并点击
err := xtDriver.TapCV("icon", "设置图标")
```
#### 智能滑动查找
```go
// 滑动查找应用并点击
err := xtDriver.SwipeToTapApp("微信")
// 滑动查找文本并点击
err := xtDriver.SwipeToTapText("设置")
// 滑动查找多个文本中的一个
err := xtDriver.SwipeToTapTexts([]string{"登录", "Sign In", "ログイン"})
```
### 组合操作
#### 登录流程
```go
func performLogin(driver IDriver, username, password string) error {
// 1. 点击用户名输入框
err := driver.TapBySelector("用户名")
if err != nil {
return err
}
// 2. 输入用户名
err = driver.Input(username)
if err != nil {
return err
}
// 3. 点击密码输入框
err = driver.TapBySelector("密码")
if err != nil {
return err
}
// 4. 输入密码
err = driver.Input(password)
if err != nil {
return err
}
// 5. 点击登录按钮
err = driver.TapBySelector("登录")
if err != nil {
return err
}
return nil
}
```
#### 列表滚动查找
```go
func findInList(driver IDriver, targetText string) error {
maxSwipes := 10
for i := 0; i < maxSwipes; i++ {
// 尝试点击目标文本
err := driver.TapBySelector(targetText)
if err == nil {
return nil // 找到并点击成功
}
// 向上滑动继续查找
err = driver.Swipe(0.5, 0.8, 0.5, 0.2)
if err != nil {
return err
}
// 等待滑动完成
time.Sleep(500 * time.Millisecond)
}
return fmt.Errorf("text '%s' not found after %d swipes", targetText, maxSwipes)
}
```
#### 表单填写
```go
func fillForm(driver IDriver, formData map[string]string) error {
for fieldName, value := range formData {
// 点击字段
err := driver.TapBySelector(fieldName)
if err != nil {
return fmt.Errorf("failed to tap field %s: %w", fieldName, err)
}
// 清空现有内容
err = driver.Backspace(50)
if err != nil {
return fmt.Errorf("failed to clear field %s: %w", fieldName, err)
}
// 输入新值
err = driver.Input(value)
if err != nil {
return fmt.Errorf("failed to input value for field %s: %w", fieldName, err)
}
}
return nil
}
```
## 应用管理
### 应用生命周期
#### 启动应用
```go
// 启动应用
err := driver.AppLaunch("com.example.app")
// 启动系统应用
err := driver.AppLaunch("com.android.settings") // Android 设置
err := driver.AppLaunch("com.apple.Preferences") // iOS 设置
```
#### 终止应用
```go
// 终止应用
terminated, err := driver.AppTerminate("com.example.app")
if err != nil {
return err
}
if terminated {
fmt.Println("App terminated successfully")
} else {
fmt.Println("App was not running")
}
```
#### 清理应用数据
```go
// 清理应用数据和缓存Android
err := driver.AppClear("com.example.app")
```
### 应用信息
#### 获取前台应用
```go
// 获取当前前台应用信息
appInfo, err := driver.ForegroundInfo()
if err != nil {
return err
}
fmt.Printf("Current app: %s (%s)\n", appInfo.Name, appInfo.PackageName)
```
#### 列出已安装应用
```go
// 列出所有已安装的应用(需要扩展功能)
packages, err := xtDriver.ListPackages()
if err != nil {
return err
}
for _, pkg := range packages {
fmt.Printf("Package: %s\n", pkg)
}
```
## 屏幕操作
### 截图操作
#### 基础截图
```go
// 获取屏幕截图
screenshot, err := driver.ScreenShot()
if err != nil {
return err
}
// 保存截图到文件
err = ioutil.WriteFile("screenshot.png", screenshot.Bytes(), 0644)
```
#### 带选项的截图
```go
// 高质量截图
screenshot, err := driver.ScreenShot(
option.WithQuality(100),
)
// 指定格式截图
screenshot, err := driver.ScreenShot(
option.WithFormat("jpeg"),
)
```
### 屏幕录制
```go
// 开始录制
videoPath, err := driver.ScreenRecord(
option.WithDuration(30*time.Second),
option.WithBitRate(4000000),
)
if err != nil {
return err
}
fmt.Printf("Video saved to: %s\n", videoPath)
```
### 屏幕信息
#### 获取屏幕尺寸
```go
// 获取屏幕尺寸
size, err := driver.WindowSize()
if err != nil {
return err
}
fmt.Printf("Screen size: %dx%d\n", size.Width, size.Height)
```
#### 获取屏幕方向
```go
// 获取当前方向
orientation, err := driver.Orientation()
if err != nil {
return err
}
fmt.Printf("Orientation: %s\n", orientation)
// 获取旋转角度
rotation, err := driver.Rotation()
if err != nil {
return err
}
fmt.Printf("Rotation: %d degrees\n", rotation)
```
#### 设置屏幕方向
```go
// 设置为横屏
err := driver.SetRotation(types.RotationLandscape)
// 设置为竖屏
err := driver.SetRotation(types.RotationPortrait)
// 设置为倒置横屏
err := driver.SetRotation(types.RotationLandscapeFlipped)
```
## 文件操作
### 文件传输
#### 推送文件到设备
```go
// 推送单个文件
err := driver.PushFile("/local/path/file.txt", "/sdcard/Download/")
// 推送图片
err := driver.PushImage("/local/path/image.jpg")
```
#### 从设备拉取文件
```go
// 拉取文件到本地
err := driver.PullFiles("/local/download/", "/sdcard/Download/")
// 拉取图片
err := driver.PullImages("/local/images/")
```
#### 清理文件
```go
// 清理指定路径的文件
err := driver.ClearFiles("/sdcard/Download/temp.txt")
// 清理图片
err := driver.ClearImages()
```
## Web 操作
### 页面导航
```go
// 导航到URL仅Web驱动
if webDriver, ok := driver.(*BrowserDriver); ok {
err := webDriver.NavigateTo("https://example.com")
// 刷新页面
err = webDriver.Refresh()
// 后退
err = webDriver.GoBack()
// 前进
err = webDriver.GoForward()
}
```
### 元素操作
#### 悬停操作
```go
// 悬停在元素上主要用于Web
err := driver.HoverBySelector("#menu-item")
// 悬停在坐标上
err := driver.HoverXY(0.5, 0.3)
```
#### 右键点击
```go
// 右键点击坐标
err := driver.SecondaryClick(100, 200)
// 右键点击元素
err := driver.SecondaryClickBySelector("#context-menu-target")
```
### JavaScript 执行
```go
// 执行JavaScript仅Web驱动
if webDriver, ok := driver.(*BrowserDriver); ok {
result, err := webDriver.ExecuteScript("return document.title;")
if err == nil {
fmt.Printf("Page title: %s\n", result)
}
// 执行复杂脚本
script := `
var element = document.getElementById('target');
element.style.backgroundColor = 'red';
return element.innerText;
`
result, err = webDriver.ExecuteScript(script)
}
```
## 等待和同步
### 显式等待
```go
// 等待元素出现
err := waitForElement(driver, "登录", 10*time.Second)
func waitForElement(driver IDriver, selector string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
err := driver.TapBySelector(selector)
if err == nil {
return nil // 元素找到
}
time.Sleep(500 * time.Millisecond)
}
return fmt.Errorf("element '%s' not found within %v", selector, timeout)
}
```
### 条件等待
```go
// 等待条件满足
err := waitForCondition(func() bool {
// 检查某个条件
appInfo, err := driver.ForegroundInfo()
return err == nil && appInfo.PackageName == "com.target.app"
}, 30*time.Second)
func waitForCondition(condition func() bool, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if condition() {
return nil
}
time.Sleep(1 * time.Second)
}
return fmt.Errorf("condition not met within %v", timeout)
}
```
### 智能等待
```go
// 等待页面加载完成
func waitForPageLoad(driver IDriver) error {
// 等待一段时间让页面开始加载
time.Sleep(1 * time.Second)
// 连续检查页面是否稳定
var lastScreenshot []byte
stableCount := 0
for i := 0; i < 10; i++ {
screenshot, err := driver.ScreenShot()
if err != nil {
return err
}
currentScreenshot := screenshot.Bytes()
if lastScreenshot != nil && bytes.Equal(lastScreenshot, currentScreenshot) {
stableCount++
if stableCount >= 3 {
return nil // 页面稳定
}
} else {
stableCount = 0
}
lastScreenshot = currentScreenshot
time.Sleep(1 * time.Second)
}
return fmt.Errorf("page did not stabilize")
}
```
## 错误处理
### 重试机制
```go
// 带重试的操作
func performWithRetry(operation func() error, maxRetries int) error {
var lastErr error
for i := 0; i < maxRetries; i++ {
err := operation()
if err == nil {
return nil
}
lastErr = err
// 指数退避
waitTime := time.Duration(math.Pow(2, float64(i))) * time.Second
time.Sleep(waitTime)
}
return fmt.Errorf("operation failed after %d retries: %w", maxRetries, lastErr)
}
// 使用示例
err := performWithRetry(func() error {
return driver.TapBySelector("登录")
}, 3)
```
### 异常恢复
```go
// 操作失败时的恢复策略
func performWithRecovery(driver IDriver, operation func() error) error {
err := operation()
if err == nil {
return nil
}
// 尝试恢复策略
log.Warn().Err(err).Msg("operation failed, attempting recovery")
// 策略1: 返回主屏幕
if err := driver.Home(); err != nil {
log.Error().Err(err).Msg("failed to go home")
}
// 策略2: 等待一段时间
time.Sleep(2 * time.Second)
// 策略3: 重新尝试操作
return operation()
}
```
## 性能优化
### 批量操作
```go
// 批量执行操作以提高性能
func performBatchOperations(driver IDriver, operations []func() error) error {
// 如果驱动支持批量模式
if batchDriver, ok := driver.(interface{ BeginBatch(); EndBatch() }); ok {
batchDriver.BeginBatch()
defer batchDriver.EndBatch()
}
for i, operation := range operations {
err := operation()
if err != nil {
return fmt.Errorf("batch operation %d failed: %w", i, err)
}
}
return nil
}
```
### 缓存优化
```go
// 缓存屏幕截图以避免重复获取
type ScreenshotCache struct {
screenshot *bytes.Buffer
timestamp time.Time
ttl time.Duration
}
func (c *ScreenshotCache) GetScreenshot(driver IDriver) (*bytes.Buffer, error) {
if c.screenshot != nil && time.Since(c.timestamp) < c.ttl {
return c.screenshot, nil
}
screenshot, err := driver.ScreenShot()
if err != nil {
return nil, err
}
c.screenshot = screenshot
c.timestamp = time.Now()
return screenshot, nil
}
```
## 最佳实践
### 1. 操作前检查
```go
// 操作前检查设备状态
func checkDeviceReady(driver IDriver) error {
status, err := driver.Status()
if err != nil {
return fmt.Errorf("failed to get device status: %w", err)
}
if status.State != "online" {
return fmt.Errorf("device not ready: %s", status.State)
}
return nil
}
```
### 2. 操作后验证
```go
// 操作后验证结果
func tapAndVerify(driver IDriver, selector string, expectedResult func() bool) error {
err := driver.TapBySelector(selector)
if err != nil {
return err
}
// 等待操作生效
time.Sleep(1 * time.Second)
// 验证结果
if !expectedResult() {
return fmt.Errorf("tap operation did not produce expected result")
}
return nil
}
```
### 3. 资源清理
```go
// 确保资源清理
func performOperationWithCleanup(driver IDriver, operation func() error) error {
// 记录初始状态
initialApp, _ := driver.ForegroundInfo()
defer func() {
// 恢复到初始状态
if initialApp != nil {
driver.AppLaunch(initialApp.PackageName)
}
}()
return operation()
}
```
### 4. 日志记录
```go
// 详细的操作日志
func loggedTap(driver IDriver, x, y float64) error {
log.Info().
Float64("x", x).
Float64("y", y).
Msg("performing tap operation")
start := time.Now()
err := driver.TapXY(x, y)
elapsed := time.Since(start)
if err != nil {
log.Error().
Err(err).
Float64("x", x).
Float64("y", y).
Dur("elapsed", elapsed).
Msg("tap operation failed")
} else {
log.Info().
Float64("x", x).
Float64("y", y).
Dur("elapsed", elapsed).
Msg("tap operation completed")
}
return err
}
```
## 参考资料
- [Android UiAutomator2 文档](https://developer.android.com/training/testing/ui-automator)
- [iOS WebDriverAgent 文档](https://github.com/appium/WebDriverAgent)
- [WebDriver 规范](https://w3c.github.io/webdriver/)
- [Appium 文档](https://appium.io/docs/)

699
docs/uixt/options.md Normal file
View File

@@ -0,0 +1,699 @@
# 配置选项文档
## 概述
HttpRunner UIXT 提供了丰富的配置选项支持设备配置、驱动配置、AI 服务配置等多个层面的定制化设置。本文档详细介绍所有可用的配置选项。
## 设备配置选项
### Android 设备配置
#### 基础选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithSerialNumber` | string | 设备序列号 | 必需 | `"emulator-5554"` |
| `WithAdbLogOn` | bool | 启用 ADB 日志 | false | `true` |
| `WithReset` | bool | 重置设备状态 | false | `true` |
```go
device, err := uixt.NewAndroidDevice(
option.WithSerialNumber("emulator-5554"),
option.WithAdbLogOn(true),
option.WithReset(true),
)
```
#### 网络选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithSystemPort` | int | UiAutomator2 系统端口 | 8200 | `8200` |
| `WithDevicePort` | int | 设备端口 | 6790 | `6790` |
| `WithForwardPort` | int | 端口转发 | 0 | `8080` |
| `WithProxy` | string | 代理设置 | "" | `"http://proxy:8080"` |
```go
device, err := uixt.NewAndroidDevice(
option.WithSerialNumber("device_serial"),
option.WithSystemPort(8200),
option.WithDevicePort(6790),
option.WithForwardPort(8080),
option.WithProxy("http://proxy.example.com:8080"),
)
```
#### 应用管理选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithInstallApp` | string | 自动安装应用路径 | "" | `"/path/to/app.apk"` |
| `WithGrantPermissions` | bool | 自动授予权限 | false | `true` |
| `WithSkipServerInstallation` | bool | 跳过服务器安装 | false | `true` |
| `WithUiAutomator2Timeout` | int | UiAutomator2 超时(秒) | 60 | `120` |
```go
device, err := uixt.NewAndroidDevice(
option.WithSerialNumber("device_serial"),
option.WithInstallApp("/path/to/app.apk"),
option.WithGrantPermissions(true),
option.WithUiAutomator2Timeout(120),
)
```
### iOS 设备配置
#### 基础选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithUDID` | string | 设备 UDID | 必需 | `"00008030-001234567890123A"` |
| `WithWDAPort` | int | WebDriverAgent 端口 | 8700 | `8700` |
| `WithWDAMjpegPort` | int | MJPEG 流端口 | 8800 | `8800` |
```go
device, err := uixt.NewIOSDevice(
option.WithUDID("00008030-001234567890123A"),
option.WithWDAPort(8700),
option.WithWDAMjpegPort(8800),
)
```
#### WDA 配置选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithResetHomeOnStartup` | bool | 启动时回到主屏 | true | `false` |
| `WithPreventWDAAttachments` | bool | 防止 WDA 附件 | false | `true` |
| `WithWDAStartupTimeout` | int | WDA 启动超时(秒) | 120 | `180` |
| `WithWDAConnectionTimeout` | int | WDA 连接超时(秒) | 60 | `90` |
```go
device, err := uixt.NewIOSDevice(
option.WithUDID("device_udid"),
option.WithResetHomeOnStartup(false),
option.WithPreventWDAAttachments(true),
option.WithWDAStartupTimeout(180),
option.WithWDAConnectionTimeout(90),
)
```
### HarmonyOS 设备配置
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithConnectKey` | string | 设备连接密钥 | 必需 | `"192.168.1.100:5555"` |
| `WithHDCLogOn` | bool | 启用 HDC 日志 | false | `true` |
| `WithSystemPort` | int | 系统端口 | 9200 | `9200` |
```go
device, err := uixt.NewHarmonyDevice(
option.WithConnectKey("192.168.1.100:5555"),
option.WithHDCLogOn(true),
option.WithSystemPort(9200),
)
```
### Web 浏览器配置
#### 基础选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithBrowserID` | string | 浏览器标识 | 必需 | `"chrome"` |
| `WithHeadless` | bool | 无头模式 | true | `false` |
| `WithWindowSize` | int, int | 窗口大小 | 1280x720 | `1920, 1080` |
```go
device, err := uixt.NewBrowserDevice(
option.WithBrowserID("chrome"),
option.WithHeadless(false),
option.WithWindowSize(1920, 1080),
)
```
#### 高级选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithUserAgent` | string | 自定义 User-Agent | 默认 | `"custom-agent"` |
| `WithProxy` | string | 代理地址 | 无 | `"http://proxy:8080"` |
| `WithExtensions` | []string | 扩展列表 | 无 | `[]string{"ext1", "ext2"}` |
| `WithDownloadDir` | string | 下载目录 | 默认 | `"/path/to/downloads"` |
```go
device, err := uixt.NewBrowserDevice(
option.WithBrowserID("chrome"),
option.WithUserAgent("custom-agent"),
option.WithProxy("http://proxy:8080"),
option.WithExtensions([]string{"extension1", "extension2"}),
option.WithDownloadDir("/custom/download/path"),
)
```
## AI 服务配置
### LLM 服务配置
#### 基础配置
```go
// 使用单一模型
xtDriver, err := uixt.NewXTDriver(driver,
option.WithLLMService(option.OPENAI_GPT_4O),
)
```
#### 高级配置
```go
// 混合模型配置
config := option.NewLLMServiceConfig(option.DOUBAO_1_5_THINKING_VISION_PRO_250428).
WithPlannerModel(option.DOUBAO_1_5_UI_TARS_250328).
WithAsserterModel(option.OPENAI_GPT_4O).
WithQuerierModel(option.DEEPSEEK_R1_250528)
xtDriver, err := uixt.NewXTDriver(driver,
option.WithLLMConfig(config),
)
```
#### 支持的模型
| 模型名称 | 特点 | 适用场景 |
|---------|------|----------|
| `DOUBAO_1_5_UI_TARS_250328` | UI 理解专业模型 | UI 元素识别和操作规划 |
| `DOUBAO_1_5_THINKING_VISION_PRO_250428` | 思考推理模型 | 复杂逻辑推理和断言 |
| `OPENAI_GPT_4O` | 高性能通用模型 | 全场景通用 |
| `DEEPSEEK_R1_250528` | 成本效益模型 | 大量查询场景 |
#### 推荐配置
```go
configs := option.RecommendedConfigurations()
// 混合优化配置(推荐)
config := configs["mixed_optimal"]
// 高性能配置
config := configs["high_performance"]
// 成本优化配置
config := configs["cost_effective"]
// UI 专注配置
config := configs["ui_focused"]
// 推理专注配置
config := configs["reasoning_focused"]
```
### CV 服务配置
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithCVService` | CVServiceType | CV 服务类型 | 无 | `option.CVServiceTypeVEDEM` |
```go
xtDriver, err := uixt.NewXTDriver(driver,
option.WithCVService(option.CVServiceTypeVEDEM),
)
```
## 操作配置选项
### 通用操作选项
#### 时间相关选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithDuration` | time.Duration | 操作持续时间 | 默认 | `2*time.Second` |
| `WithTimeout` | time.Duration | 操作超时时间 | 30s | `60*time.Second` |
| `WithDelay` | time.Duration | 操作前延迟 | 0 | `500*time.Millisecond` |
```go
// 慢速滑动
err := driver.Swipe(0.5, 0.8, 0.5, 0.2,
option.WithDuration(2*time.Second),
)
// 长按操作
err := driver.TouchAndHold(150, 300,
option.WithDuration(3*time.Second),
)
// 带超时的操作
err := driver.TapBySelector("登录",
option.WithTimeout(10*time.Second),
)
```
#### 精度相关选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithSteps` | int | 滑动步数 | 默认 | `20` |
| `WithPressure` | float64 | 压力值(iOS) | 1.0 | `0.8` |
| `WithFrequency` | int | 操作频率 | 默认 | `60` |
```go
// 多步滑动
err := driver.Swipe(0.5, 0.8, 0.5, 0.2,
option.WithSteps(50),
)
// 3D Touch (iOS)
err := driver.ForceTouch(100, 200,
option.WithPressure(0.8),
)
```
### 截图选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithQuality` | int | 图片质量 | 80 | `100` |
| `WithFormat` | string | 图片格式 | "png" | `"jpeg"` |
| `WithScale` | float64 | 缩放比例 | 1.0 | `0.5` |
```go
// 高质量截图
screenshot, err := driver.ScreenShot(
option.WithQuality(100),
option.WithFormat("png"),
)
// 缩放截图
screenshot, err := driver.ScreenShot(
option.WithScale(0.5),
)
```
### 录制选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithBitRate` | int | 比特率 | 4000000 | `8000000` |
| `WithVideoSize` | string | 视频尺寸 | 默认 | `"1280x720"` |
| `WithTimeLimit` | time.Duration | 录制时长 | 180s | `300*time.Second` |
```go
// 高质量录制
videoPath, err := driver.ScreenRecord(
option.WithBitRate(8000000),
option.WithVideoSize("1920x1080"),
option.WithTimeLimit(300*time.Second),
)
```
### OCR 选项
| 选项 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| `WithRegex` | bool | 使用正则表达式 | false | `true` |
| `WithIndex` | int | 文本索引 | 0 | `1` |
| `WithIgnoreCase` | bool | 忽略大小写 | false | `true` |
```go
// 正则表达式匹配
err := xtDriver.TapOCR(`\d{4}`,
option.WithRegex(true),
)
// 选择第二个匹配项
err := xtDriver.TapOCR("按钮",
option.WithIndex(1),
)
// 忽略大小写
err := xtDriver.TapOCR("LOGIN",
option.WithIgnoreCase(true),
)
```
## 环境变量配置
### LLM 模型配置
#### 豆包模型
```bash
# 豆包思维视觉专业版
DOUBAO_1_5_THINKING_VISION_PRO_250428_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
DOUBAO_1_5_THINKING_VISION_PRO_250428_API_KEY=your_doubao_api_key
# 豆包UI-TARS
DOUBAO_1_5_UI_TARS_250328_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
DOUBAO_1_5_UI_TARS_250328_API_KEY=your_doubao_ui_tars_api_key
```
#### OpenAI 模型
```bash
# OpenAI GPT-4O
OPENAI_GPT_4O_BASE_URL=https://api.openai.com/v1
OPENAI_GPT_4O_API_KEY=your_openai_api_key
```
#### DeepSeek 模型
```bash
# DeepSeek
DEEPSEEK_R1_250528_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_R1_250528_API_KEY=your_deepseek_api_key
```
#### 默认配置
```bash
# 默认配置,当没有找到服务特定配置时使用
LLM_MODEL_NAME=doubao-1.5-thinking-vision-pro-250428
OPENAI_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
OPENAI_API_KEY=your_default_api_key
```
### CV 服务配置
#### 火山引擎 VEDEM
```bash
# 火山引擎 VEDEM 配置
VEDEM_IMAGE_URL=https://visual.volcengineapi.com
VEDEM_IMAGE_AK=your_access_key
VEDEM_IMAGE_SK=your_secret_key
```
### 配置优先级
环境变量的加载优先级(从高到低):
1. `.env` 文件(当前工作目录)
2. `~/.hrp/.env` 文件(全局用户配置)
3. 系统环境变量
```bash
# 项目级配置文件 .env
OPENAI_API_KEY=project_specific_key
# 用户级配置文件 ~/.hrp/.env
OPENAI_API_KEY=user_default_key
# 系统环境变量
export OPENAI_API_KEY=system_key
```
## 配置文件
### 项目配置文件
创建 `.env` 文件在项目根目录:
```bash
# .env
# LLM 服务配置
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_API_KEY=your_openai_api_key
# CV 服务配置
VEDEM_IMAGE_URL=https://visual.volcengineapi.com
VEDEM_IMAGE_AK=your_access_key
VEDEM_IMAGE_SK=your_secret_key
# 设备配置
DEFAULT_ANDROID_SERIAL=emulator-5554
DEFAULT_IOS_UDID=00008030-001234567890123A
```
### 用户配置文件
创建 `~/.hrp/.env` 文件:
```bash
# ~/.hrp/.env
# 全局默认配置
OPENAI_API_KEY=your_global_api_key
VEDEM_IMAGE_AK=your_global_access_key
VEDEM_IMAGE_SK=your_global_secret_key
```
### YAML 配置文件
```yaml
# config.yaml
devices:
android:
serial: "emulator-5554"
system_port: 8200
device_port: 6790
adb_log: true
ios:
udid: "00008030-001234567890123A"
wda_port: 8700
mjpeg_port: 8800
reset_home: false
ai_services:
llm:
default_model: "doubao-1.5-thinking-vision-pro-250428"
planner_model: "doubao-1.5-ui-tars-250328"
asserter_model: "openai-gpt-4o"
querier_model: "deepseek-r1-250528"
cv:
service_type: "vedem"
operations:
default_timeout: 30
screenshot_quality: 80
video_bitrate: 4000000
```
## 动态配置
### 运行时配置
```go
// 运行时修改配置
func configureDriver(driver IDriver) error {
// 设置超时
driver.SetTimeout(60 * time.Second)
// 设置重试次数
driver.SetRetryCount(3)
// 设置日志级别
driver.SetLogLevel(log.DebugLevel)
return nil
}
```
### 条件配置
```go
// 根据环境选择配置
func createDriverWithEnvironmentConfig(platform string) (*uixt.XTDriver, error) {
var device uixt.IDevice
var err error
switch platform {
case "android":
if os.Getenv("CI") == "true" {
// CI 环境使用模拟器
device, err = uixt.NewAndroidDevice(
option.WithSerialNumber("emulator-5554"),
option.WithReset(true),
)
} else {
// 本地环境使用真机
device, err = uixt.NewAndroidDevice(
option.WithSerialNumber(os.Getenv("ANDROID_SERIAL")),
option.WithAdbLogOn(true),
)
}
}
if err != nil {
return nil, err
}
driver, err := uixt.NewUIA2Driver(device)
if err != nil {
return nil, err
}
// 根据环境选择 AI 配置
var aiOptions []option.AIServiceOption
if os.Getenv("ENABLE_AI") == "true" {
configs := option.RecommendedConfigurations()
aiOptions = append(aiOptions, option.WithLLMConfig(configs["mixed_optimal"]))
aiOptions = append(aiOptions, option.WithCVService(option.CVServiceTypeVEDEM))
}
return uixt.NewXTDriver(driver, aiOptions...)
}
```
## 配置验证
### 配置检查
```go
// 验证配置完整性
func validateConfiguration() error {
// 检查必需的环境变量
requiredEnvs := []string{
"OPENAI_API_KEY",
"VEDEM_IMAGE_AK",
"VEDEM_IMAGE_SK",
}
for _, env := range requiredEnvs {
if os.Getenv(env) == "" {
return fmt.Errorf("required environment variable %s not set", env)
}
}
// 检查设备连接
devices, err := uixt.DiscoverAndroidDevices()
if err != nil {
return fmt.Errorf("failed to discover Android devices: %w", err)
}
if len(devices) == 0 {
return fmt.Errorf("no Android devices found")
}
return nil
}
```
### 配置诊断
```go
// 配置诊断工具
func diagnoseConfiguration() {
fmt.Println("=== Configuration Diagnosis ===")
// 检查环境变量
fmt.Println("\nEnvironment Variables:")
envVars := []string{
"OPENAI_BASE_URL", "OPENAI_API_KEY",
"VEDEM_IMAGE_URL", "VEDEM_IMAGE_AK", "VEDEM_IMAGE_SK",
}
for _, env := range envVars {
value := os.Getenv(env)
if value != "" {
fmt.Printf(" %s: %s\n", env, maskSensitive(value))
} else {
fmt.Printf(" %s: NOT SET\n", env)
}
}
// 检查设备连接
fmt.Println("\nDevice Status:")
androidDevices, _ := uixt.DiscoverAndroidDevices()
fmt.Printf(" Android devices: %d\n", len(androidDevices))
iosDevices, _ := uixt.DiscoverIOSDevices()
fmt.Printf(" iOS devices: %d\n", len(iosDevices))
}
func maskSensitive(value string) string {
if len(value) <= 8 {
return "***"
}
return value[:4] + "***" + value[len(value)-4:]
}
```
## 最佳实践
### 1. 配置分层
```go
// 分层配置管理
type Config struct {
Device DeviceConfig `yaml:"device"`
AI AIConfig `yaml:"ai"`
Operation OperationConfig `yaml:"operation"`
}
type DeviceConfig struct {
Platform string `yaml:"platform"`
Serial string `yaml:"serial"`
Timeout int `yaml:"timeout"`
}
type AIConfig struct {
LLMModel string `yaml:"llm_model"`
CVService string `yaml:"cv_service"`
}
type OperationConfig struct {
DefaultTimeout int `yaml:"default_timeout"`
RetryCount int `yaml:"retry_count"`
}
```
### 2. 配置验证
```go
// 配置验证
func (c *Config) Validate() error {
if c.Device.Platform == "" {
return fmt.Errorf("device platform is required")
}
if c.Device.Serial == "" {
return fmt.Errorf("device serial is required")
}
if c.Operation.DefaultTimeout <= 0 {
c.Operation.DefaultTimeout = 30 // 设置默认值
}
return nil
}
```
### 3. 配置热重载
```go
// 配置热重载
func watchConfigFile(configPath string, callback func(*Config)) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
err = watcher.Add(configPath)
if err != nil {
log.Fatal(err)
}
for {
select {
case event := <-watcher.Events:
if event.Op&fsnotify.Write == fsnotify.Write {
config, err := loadConfig(configPath)
if err == nil {
callback(config)
}
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}
```
## 参考资料
- [环境变量最佳实践](https://12factor.net/config)
- [YAML 配置文件格式](https://yaml.org/)
- [Go 配置管理库 Viper](https://github.com/spf13/viper)

View File

@@ -1 +1 @@
v5.0.0-beta-2506111218
v5.0.0-beta-2506111457

View File

@@ -1,34 +0,0 @@
# uixt
From v4.3.0HttpRunner will support mobile UI automation testing:
- iOS: based on [appium/WebDriverAgent], with forked client library [electricbubble/gwda] in golang
- Android: based on [appium-uiautomator2-server], with forked client library [electricbubble/guia2] in golang
Some UI recognition algorithms are also introduced for both iOS and Android:
- OCR: based on OCR API service from [volcengine], other API service may be extended
## Dependencies
### OCR
OCR API is a paid service, you need to pre-purchase and configure the environment variables.
- VEDEM_IMAGE_URL
- VEDEM_IMAGE_AK
- VEDEM_IMAGE_SK
## Thanks
This uixt module is initially forked from the following repos and made a lot of changes.
- [electricbubble/gwda]
- [electricbubble/guia2]
[appium/WebDriverAgent]: https://github.com/appium/WebDriverAgent
[electricbubble/gwda]: https://github.com/electricbubble/gwda
[electricbubble/guia2]: https://github.com/electricbubble/guia2
[volcengine]: https://www.volcengine.com/product/text-recognition
[appium-uiautomator2-server]: https://github.com/appium/appium-uiautomator2-server

View File

@@ -1,299 +0,0 @@
# HttpRunner AI Querier - 自定义输出格式功能
## 功能概述
HttpRunner 的 AI Querier 模块支持自定义输出格式功能,允许用户指定特定的数据结构,让 AI 模型返回结构化的数据响应。适用于:
- **UI 元素分析**:自动化测试中的界面元素提取
- **游戏界面分析**网格类游戏连连看、消消乐、2048等数据提取
- **表单数据提取**:从表单截图中提取结构化信息
- **图像内容分析**:任何需要从截图中提取结构化信息的场景
## 核心数据结构
```go
// QueryOptions - 查询选项
type QueryOptions struct {
Query string `json:"query"` // 查询文本
Screenshot string `json:"screenshot"` // Base64编码的截图
Size types.Size `json:"size"` // 屏幕尺寸
OutputSchema interface{} `json:"outputSchema,omitempty"` // 自定义输出格式(可选)
}
// QueryResult - 查询结果
type QueryResult struct {
Content string `json:"content"` // 人类可读的分析结果
Thought string `json:"thought"` // AI 推理过程
Data interface{} `json:"data,omitempty"` // 结构化数据使用OutputSchema时自动转换为指定类型
}
```
## 基本用法
### 标准查询
```go
// 创建查询器
modelConfig, err := ai.GetModelConfig(option.OPENAI_GPT_4O)
querier, err := ai.NewQuerier(ctx, modelConfig)
// 执行查询
result, err := querier.Query(ctx, &ai.QueryOptions{
Query: "请分析这张截图中的内容",
Screenshot: screenshot,
Size: size,
// 不指定 OutputSchema
})
fmt.Printf("分析结果: %s\n", result.Content)
fmt.Printf("推理过程: %s\n", result.Thought)
// result.Data 为 nil
```
### 自定义格式查询
```go
// 定义输出结构
type GameAnalysis struct {
Content string `json:"content"` // 分析描述
Thought string `json:"thought"` // 思考过程
Rows int `json:"rows"` // 行数
Cols int `json:"cols"` // 列数
Icons []string `json:"icons"` // 图标类型
}
// 执行查询
result, err := querier.Query(ctx, &ai.QueryOptions{
Query: "分析这个游戏界面的网格结构和图标类型",
Screenshot: screenshot,
Size: size,
OutputSchema: GameAnalysis{}, // 指定输出格式
})
// 直接类型断言获取结构化数据
if gameData, ok := result.Data.(*GameAnalysis); ok {
fmt.Printf("行数: %d, 列数: %d\n", gameData.Rows, gameData.Cols)
fmt.Printf("图标类型: %v\n", gameData.Icons)
}
```
## 应用场景示例
### UI 元素分析
```go
type UIAnalysis struct {
Content string `json:"content"`
Thought string `json:"thought"`
Elements []UIElement `json:"elements"`
}
type UIElement struct {
Type string `json:"type"` // button, text, input等
Text string `json:"text"` // 文本内容
BoundBox BoundingBox `json:"boundBox"` // 位置坐标
Clickable bool `json:"clickable"` // 是否可点击
}
type BoundingBox struct {
X, Y, Width, Height int `json:"x,y,width,height"`
}
```
### 网格游戏分析
```go
type GridGame struct {
Content string `json:"content"`
Thought string `json:"thought"`
Grid [][]Cell `json:"grid"` // 网格数据
Stats Statistics `json:"statistics"` // 统计信息
}
type Cell struct {
Type string `json:"type"` // 单元格类型
Value string `json:"value"` // 单元格值
Row int `json:"row"` // 行索引
Col int `json:"col"` // 列索引
}
type Statistics struct {
TotalCells int `json:"totalCells"`
UniqueTypes int `json:"uniqueTypes"`
}
```
### 表单数据提取
```go
type FormAnalysis struct {
Content string `json:"content"`
Thought string `json:"thought"`
Fields []FormField `json:"fields"`
Actions []Action `json:"actions"`
}
type FormField struct {
Label string `json:"label"` // 字段标签
Type string `json:"type"` // 字段类型
Value string `json:"value"` // 当前值
Required bool `json:"required"` // 是否必填
BoundBox BoundingBox `json:"boundBox"` // 位置
}
```
## 核心特性
### 自动类型转换
- 指定 `OutputSchema` 时,`QueryResult.Data` 自动转换为指定类型
- 支持直接类型断言:`result.Data.(*YourType)`
- 无需手动调用转换函数
### 多级回退机制
1. 优先解析为指定的结构化类型
2. 失败时尝试通用JSON解析
3. 最终回退到纯文本响应
### 向后兼容
- 不指定 `OutputSchema` 时行为不变
- 现有代码无需修改
## 最佳实践
### 1. 结构体设计
```go
// 推荐:包含标准字段
type YourSchema struct {
Content string `json:"content"` // 必须:人类可读描述
Thought string `json:"thought"` // 必须AI推理过程
// 自定义字段...
Data CustomData `json:"data"`
}
// 使用描述性的JSON标签
type Element struct {
Type string `json:"elementType"` // 清晰的字段名
Position Point `json:"gridPosition"` // 描述性标签
Visible bool `json:"isVisible"` // 布尔值清晰性
}
```
### 2. 查询指令
```go
// 推荐:详细的查询指令
opts := &ai.QueryOptions{
Query: `分析这张截图并提供结构化信息:
1. 识别界面类型和主要元素
2. 提取所有可交互元素的位置和属性
3. 统计各类元素的数量`,
Screenshot: screenshot,
Size: size,
OutputSchema: YourSchema{},
}
```
### 3. 错误处理
```go
result, err := querier.Query(ctx, opts)
if err != nil {
return err
}
// 类型断言
if data, ok := result.Data.(*YourSchema); ok {
// 使用结构化数据
processData(data)
} else {
// 回退到文本结果
log.Printf("结构化解析失败,使用文本结果: %s", result.Content)
}
```
## 完整示例
```go
package main
import (
"context"
"fmt"
"log"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/uixt/ai"
"github.com/httprunner/httprunner/v5/uixt/option"
)
type ScreenAnalysis struct {
Content string `json:"content"`
Thought string `json:"thought"`
Elements []string `json:"elements"`
Categories []string `json:"categories"`
Count int `json:"count"`
}
func main() {
ctx := context.Background()
// 创建查询器
modelConfig, err := ai.GetModelConfig(option.OPENAI_GPT_4O)
if err != nil {
log.Fatal(err)
}
querier, err := ai.NewQuerier(ctx, modelConfig)
if err != nil {
log.Fatal(err)
}
// 加载截图
screenshot, size, err := builtin.LoadImage("screenshot.png")
if err != nil {
log.Fatal(err)
}
// 执行结构化查询
result, err := querier.Query(ctx, &ai.QueryOptions{
Query: "分析截图中的UI元素提取元素类型和分类信息",
Screenshot: screenshot,
Size: size,
OutputSchema: ScreenAnalysis{},
})
if err != nil {
log.Fatal(err)
}
// 使用结构化数据
if analysis, ok := result.Data.(*ScreenAnalysis); ok {
fmt.Printf("发现 %d 个元素\n", analysis.Count)
fmt.Printf("元素类型: %v\n", analysis.Elements)
fmt.Printf("分类: %v\n", analysis.Categories)
} else {
fmt.Printf("文本结果: %s\n", result.Content)
}
}
```
## 辅助函数
对于特殊情况,提供了类型转换辅助函数:
```go
// 手动类型转换(通常不需要)
converted, err := ai.ConvertQueryResultData[YourType](result)
if err != nil {
return err
}
```
**注意**:使用 `OutputSchema` 时,`Data` 字段已自动转换为正确类型,通常不需要手动调用此函数。
## 技术限制
- 需要支持结构化输出的AI模型如 OpenAI GPT-4
- 复杂嵌套结构需要清晰的查询指令
- AI模型可能不总是严格遵循指定格式
- UI-TARS 模型使用不同的响应格式处理