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

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/)