refactor: move action options to pkg/uixt/options/action

This commit is contained in:
lilong.129
2025-02-06 16:42:16 +08:00
parent f1ef161377
commit 0accec04a9
43 changed files with 982 additions and 911 deletions

View File

@@ -7,6 +7,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
"github.com/pkg/errors"
)
@@ -134,7 +135,7 @@ func convertCompatMobileStep(mobileUI *MobileUI) {
}
for i := 0; i < len(mobileUI.Actions); i++ {
ma := mobileUI.Actions[i]
actionOptions := uixt.NewActionOptions(ma.GetOptions()...)
actionOptions := options.NewActionOptions(ma.GetOptions()...)
// append tap_cv params to screenshot_with_ui_types option
if ma.Method == uixt.ACTION_TapByCV {
uiTypes, _ := builtin.ConvertToStringSlice(ma.Params)

View File

@@ -4,7 +4,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -26,8 +25,8 @@ func TestAndroidDouyinE2E(t *testing.T) {
Home().
SwipeToTapApp(
"抖音",
uixt.WithMaxRetryTimes(5),
uixt.WithTapOffset(0, -50),
options.WithMaxRetryTimes(5),
options.WithTapOffset(0, -50),
).
Sleep(20).
Validate().
@@ -48,11 +47,11 @@ func TestAndroidDouyinE2E(t *testing.T) {
Android().
TapByOCR(
"直播中",
uixt.WithIgnoreNotFoundError(true),
uixt.WithIndex(-1),
options.WithIgnoreNotFoundError(true),
options.WithIndex(-1),
).
EndToEndDelay(uixt.WithInterval(5), uixt.WithTimeout(120)).
TapByUITypes(uixt.WithScreenShotUITypes("close")),
EndToEndDelay(options.WithInterval(5), options.WithTimeout(120)).
TapByUITypes(options.WithScreenShotUITypes("close")),
},
}

View File

@@ -59,7 +59,7 @@ func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) {
}
// 进入推荐页
err = driver.TapByOCR("推荐", uixt.WithScope(0, 0, 1, 0.3))
err = driver.TapByOCR("推荐", options.WithScope(0, 0, 1, 0.3))
if err != nil {
return nil, err
}
@@ -92,7 +92,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
// 切换横屏
err = driver.TapByUIDetection(
uixt.WithScreenShotUITypes("fullScreen"))
options.WithScreenShotUITypes("fullScreen"))
if err != nil {
// 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed
// 退出回到推荐页

View File

@@ -59,7 +59,7 @@ func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) {
}
// 进入推荐页
err = driver.TapByOCR("推荐", uixt.WithScope(0, 0, 1, 0.3))
err = driver.TapByOCR("推荐", options.WithScope(0, 0, 1, 0.3))
if err != nil {
return nil, err
}
@@ -92,7 +92,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
// 切换横屏
err = driver.TapByUIDetection(
uixt.WithScreenShotUITypes("fullScreen"))
options.WithScreenShotUITypes("fullScreen"))
if err != nil {
// 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed
// 退出回到推荐页

View File

@@ -6,7 +6,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -27,7 +26,7 @@ func TestAndroidDouyinFeedTest(t *testing.T) {
AssertAppInForeground("com.ss.android.ugc.aweme"),
hrp.NewStep("处理青少年弹窗").
Android().
TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)),
TapByOCR("我知道了", options.WithIgnoreNotFoundError(true)),
hrp.NewStep("滑动 Feed 3 次,随机间隔 0-5s").
Loop(3).
Android().

View File

@@ -6,7 +6,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -27,10 +26,10 @@ func TestAndroidLiveSwipeTest(t *testing.T) {
AssertAppInForeground("com.ss.android.ugc.aweme"),
hrp.NewStep("处理青少年弹窗").
Android().
TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)),
TapByOCR("我知道了", options.WithIgnoreNotFoundError(true)),
hrp.NewStep("在推荐页上划,直到出现「点击进入直播间」").
Android().
SwipeToTapText("点击进入直播间", uixt.WithMaxRetryTimes(10), uixt.WithIdentifier("进入直播间")),
SwipeToTapText("点击进入直播间", options.WithMaxRetryTimes(10), options.WithIdentifier("进入直播间")),
hrp.NewStep("滑动 Feed 5 次60% 随机间隔 0-5s40% 随机间隔 5-10s").
Loop(5).
Android().
@@ -38,8 +37,8 @@ func TestAndroidLiveSwipeTest(t *testing.T) {
SleepRandom(0, 5, 0.6, 5, 10, 0.4),
hrp.NewStep("向上滑动,等待 10s").
Android().
SwipeUp(uixt.WithIdentifier("第一次上划")).Sleep(5).ScreenShot(). // 上划 1 次,等待 5s截图保存
SwipeUp(uixt.WithIdentifier("第二次上划")).Sleep(5).ScreenShot(), // 再上划 1 次,等待 5s截图保存
SwipeUp(options.WithIdentifier("第一次上划")).Sleep(5).ScreenShot(). // 上划 1 次,等待 5s截图保存
SwipeUp(options.WithIdentifier("第二次上划")).Sleep(5).ScreenShot(), // 再上划 1 次,等待 5s截图保存
hrp.NewStep("exit").
Android().
AppTerminate("com.ss.android.ugc.aweme").

View File

@@ -6,7 +6,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -26,22 +25,34 @@ func TestIOSDouyinFollowLive(t *testing.T) {
IOS().
Home().
AppTerminate("com.ss.iphone.ugc.Aweme"). // 关闭已运行的抖音
SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithIdentifier("启动抖音")).Sleep(5).
SwipeToTapApp("$app_name",
options.WithMaxRetryTimes(5),
options.WithIdentifier("启动抖音")).
Sleep(5).
Validate().
AssertOCRExists("推荐", "抖音启动失败,「推荐」不存在"),
hrp.NewStep("处理青少年弹窗").
IOS().
TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)),
TapByOCR("我知道了", options.WithIgnoreNotFoundError(true)),
hrp.NewStep("点击首页").
IOS().
TapByOCR("首页", uixt.WithIndex(-1)).Sleep(10),
TapByOCR("首页", options.WithIndex(-1)).Sleep(10),
hrp.NewStep("点击关注页").
IOS().
TapByOCR("关注", uixt.WithIndex(1)).Sleep(10),
TapByOCR("关注", options.WithIndex(1)).Sleep(10),
hrp.NewStep("向上滑动 2 次").
IOS().SwipeToTapTexts([]string{"理肤泉", "婉宝"}, uixt.WithCustomDirection(0.6, 0.2, 0.2, 0.2), uixt.WithIdentifier("click_live")).Sleep(10).
Swipe(0.9, 0.7, 0.9, 0.3, uixt.WithIdentifier("slide_in_live"), uixt.WithOffsetRandomRange(-10, 10)).Sleep(10).ScreenShot(). // 上划 1 次,等待 10s截图保存
Swipe(0.9, 0.7, 0.9, 0.3, uixt.WithIdentifier("slide_in_live"), uixt.WithOffsetRandomRange(-10, 10)).Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s截图保存
IOS().
SwipeToTapTexts([]string{"理肤泉", "婉宝"},
options.WithCustomDirection(0.6, 0.2, 0.2, 0.2),
options.WithIdentifier("click_live")).Sleep(10).
Swipe(0.9, 0.7, 0.9, 0.3,
options.WithIdentifier("slide_in_live"),
options.WithOffsetRandomRange(-10, 10)).
Sleep(10).ScreenShot(). // 上划 1 次,等待 10s截图保存
Swipe(0.9, 0.7, 0.9, 0.3,
options.WithIdentifier("slide_in_live"),
options.WithOffsetRandomRange(-10, 10)).
Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s截图保存
},
}

View File

@@ -6,7 +6,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -26,19 +25,23 @@ func TestIOSDouyinLive(t *testing.T) {
IOS().
Home().
AppTerminate("com.ss.iphone.ugc.Aweme"). // 关闭已运行的抖音
SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithIdentifier("启动抖音")).Sleep(5).
SwipeToTapApp("$app_name",
options.WithMaxRetryTimes(5),
options.WithIdentifier("启动抖音")).Sleep(5).
Validate().
AssertOCRExists("推荐", "抖音启动失败,「推荐」不存在"),
hrp.NewStep("处理青少年弹窗").
IOS().
TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)),
TapByOCR("我知道了", options.WithIgnoreNotFoundError(true)),
hrp.NewStep("向上滑动 2 次").
IOS().
SwipeUp(uixt.WithIdentifier("第一次上划")).Sleep(2).ScreenShot(). // 上划 1 次,等待 2s截图保存
SwipeUp(uixt.WithIdentifier("第二次上划")).Sleep(2).ScreenShot(), // 再上划 1 次,等待 2s截图保存
SwipeUp(options.WithIdentifier("第一次上划")).Sleep(2).ScreenShot(). // 上划 1 次,等待 2s截图保存
SwipeUp(options.WithIdentifier("第二次上划")).Sleep(2).ScreenShot(), // 再上划 1 次,等待 2s截图保存
hrp.NewStep("在推荐页上划,直到出现「点击进入直播间」").
IOS().
SwipeToTapText("点击进入直播间", uixt.WithMaxRetryTimes(10), uixt.WithIdentifier("进入直播间")),
SwipeToTapText("点击进入直播间",
options.WithMaxRetryTimes(10),
options.WithIdentifier("进入直播间")),
},
}

View File

@@ -6,7 +6,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -26,23 +25,25 @@ func TestWDALog(t *testing.T) {
IOS().
Home().
AppTerminate("com.ss.iphone.ugc.Aweme"). // 关闭已运行的抖音
SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithIdentifier("启动抖音")).Sleep(5).
SwipeToTapApp("$app_name",
options.WithMaxRetryTimes(5),
options.WithIdentifier("启动抖音")).Sleep(5).
Validate().
AssertOCRExists("推荐", "抖音启动失败,「推荐」不存在"),
hrp.NewStep("处理青少年弹窗").
IOS().
TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)),
TapByOCR("我知道了", options.WithIgnoreNotFoundError(true)),
hrp.NewStep("进入购物页").
IOS().TapByOCR("商城", uixt.WithIdentifier("点击商城")).Sleep(5),
IOS().TapByOCR("商城", options.WithIdentifier("点击商城")).Sleep(5),
hrp.NewStep("进入推荐页").
IOS().TapByOCR("推荐", uixt.WithIdentifier("点击推荐")).Sleep(5),
IOS().TapByOCR("推荐", options.WithIdentifier("点击推荐")).Sleep(5),
hrp.NewStep("向上滑动 2 次").
IOS().
SwipeUp(uixt.WithIdentifier("第 1 次上划")).Sleep(2).
SwipeUp(uixt.WithIdentifier("第 2 次上划")).Sleep(2).
SwipeUp(uixt.WithIdentifier("第 3 次上划")).Sleep(2).
TapXY(0.9, 0.1, uixt.WithIdentifier("点击进入搜索框")).Sleep(2).
Input("httprunner", uixt.WithIdentifier("输入搜索关键词")),
SwipeUp(options.WithIdentifier("第 1 次上划")).Sleep(2).
SwipeUp(options.WithIdentifier("第 2 次上划")).Sleep(2).
SwipeUp(options.WithIdentifier("第 3 次上划")).Sleep(2).
TapXY(0.9, 0.1, options.WithIdentifier("点击进入搜索框")).Sleep(2).
Input("httprunner", options.WithIdentifier("输入搜索关键词")),
},
}

View File

@@ -4,7 +4,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -45,11 +44,11 @@ func TestAndroidExpertTest(t *testing.T) {
Android().
SwipeToTapTexts(
[]string{"直播", "直播中", "点击进入直播间"},
uixt.WithCustomDirection(0.5, 0.7, 0.5, 0.3),
uixt.WithScope(0.2, 0.2, 1, 0.8),
uixt.WithMaxRetryTimes(50),
uixt.WithWaitTime(1.5),
uixt.WithIdentifier("click_live"),
options.WithCustomDirection(0.5, 0.7, 0.5, 0.3),
options.WithScope(0.2, 0.2, 1, 0.8),
options.WithMaxRetryTimes(50),
options.WithWaitTime(1.5),
options.WithIdentifier("click_live"),
),
hrp.NewStep("sleep 10s").
Android().
@@ -58,7 +57,7 @@ func TestAndroidExpertTest(t *testing.T) {
Android().
Swipe(
0.5, 0.7, 0.5, 0.3,
uixt.WithIdentifier("slide_in_live"),
options.WithIdentifier("slide_in_live"),
).
Sleep(5).
Back().
@@ -68,22 +67,22 @@ func TestAndroidExpertTest(t *testing.T) {
Android().
TapXY(
0.9, 0.08,
uixt.WithIdentifier("click_search_in_middle_page"),
options.WithIdentifier("click_search_in_middle_page"),
).
Sleep(5),
hrp.NewStep("【搜索】输入query词 input").
Android().
Input(
"$query",
uixt.WithIdentifier("input_query"),
options.WithIdentifier("input_query"),
).
Sleep(5),
hrp.NewStep("【搜索】点击搜索按钮 tap_ocr 自定义配置").
Android().
TapByOCR(
"搜索",
uixt.WithIdentifier("click_search_after_input_query"),
uixt.WithIndex(0),
options.WithIdentifier("click_search_after_input_query"),
options.WithIndex(0),
).
Sleep(5),
hrp.NewStep("选择直播页签 tap_ocr 默认配置").
@@ -98,8 +97,8 @@ func TestAndroidExpertTest(t *testing.T) {
hrp.NewStep("【生活服务】点击货架商品 tap_ocr 自定义配置").
Android().
TapByUITypes(
uixt.WithScreenShotUITypes("dyhouse", "shoppingbag"),
uixt.WithIdentifier("click_sales_rack"),
options.WithScreenShotUITypes("dyhouse", "shoppingbag"),
options.WithIdentifier("click_sales_rack"),
).
Sleep(5),
// 冷启动
@@ -112,13 +111,13 @@ func TestAndroidExpertTest(t *testing.T) {
hrp.NewStep("home 以及 swipe_to_tap_app 自定义配置").
Android().
Home().
SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithInterval(1), uixt.WithTapOffset(0, -50)).
SwipeToTapApp("$app_name", options.WithMaxRetryTimes(5), options.WithInterval(1), options.WithTapOffset(0, -50)).
Sleep(10),
hrp.NewStep("处理弹窗 close_popups 自定义配置 以及 ui_ocr exists 断言").
Android().
ClosePopups(
uixt.WithMaxRetryTimes(3),
uixt.WithInterval(2),
options.WithMaxRetryTimes(3),
options.WithInterval(2),
).
Validate().
AssertOCRExists("推荐", "进入抖音失败"),
@@ -128,7 +127,7 @@ func TestAndroidExpertTest(t *testing.T) {
Home().
AppTerminate("$bundle_id").
Sleep(3).
SwipeToTapApp("local", uixt.WithMaxRetryTimes(5)).Sleep(10),
SwipeToTapApp("local", options.WithMaxRetryTimes(5)).Sleep(10),
hrp.NewStep("screeshot 以及 sleep_random").
Loop(3).
Android().
@@ -179,11 +178,11 @@ func TestIOSExpertTest(t *testing.T) {
IOS().
SwipeToTapTexts(
[]string{"直播", "直播中", "点击进入直播间"},
uixt.WithCustomDirection(0.5, 0.7, 0.5, 0.3),
uixt.WithScope(0.2, 0.2, 1, 0.8),
uixt.WithMaxRetryTimes(50),
uixt.WithWaitTime(1.5),
uixt.WithIdentifier("click_live"),
options.WithCustomDirection(0.5, 0.7, 0.5, 0.3),
options.WithScope(0.2, 0.2, 1, 0.8),
options.WithMaxRetryTimes(50),
options.WithWaitTime(1.5),
options.WithIdentifier("click_live"),
),
hrp.NewStep("sleep 10s").
IOS().
@@ -192,7 +191,7 @@ func TestIOSExpertTest(t *testing.T) {
IOS().
Swipe(
0.5, 0.7, 0.5, 0.3,
uixt.WithIdentifier("slide_in_live"),
options.WithIdentifier("slide_in_live"),
).
Sleep(5).
Back().
@@ -202,22 +201,22 @@ func TestIOSExpertTest(t *testing.T) {
IOS().
TapXY(
0.9, 0.075,
uixt.WithIdentifier("click_search_in_middle_page"),
options.WithIdentifier("click_search_in_middle_page"),
).
Sleep(5),
hrp.NewStep("【搜索】输入query词 input").
IOS().
Input(
"$query",
uixt.WithIdentifier("input_query"),
options.WithIdentifier("input_query"),
).
Sleep(5),
hrp.NewStep("【搜索】点击搜索按钮 tap_ocr 自定义配置").
IOS().
TapByOCR(
"搜索",
uixt.WithIdentifier("click_search_after_input_query"),
uixt.WithIndex(0),
options.WithIdentifier("click_search_after_input_query"),
options.WithIndex(0),
).
Sleep(5),
// 生活服务赛道
@@ -232,8 +231,8 @@ func TestIOSExpertTest(t *testing.T) {
hrp.NewStep("【生活服务】点击货架商品 tap_ocr 自定义配置").
IOS().
TapByUITypes(
uixt.WithScreenShotUITypes("dyhouse", "shoppingbag"),
uixt.WithIdentifier("click_sales_rack"),
options.WithScreenShotUITypes("dyhouse", "shoppingbag"),
options.WithIdentifier("click_sales_rack"),
).
Sleep(5),
// 冷启动
@@ -245,13 +244,13 @@ func TestIOSExpertTest(t *testing.T) {
hrp.NewStep("home 以及 swipe_to_tap_app 自定义配置").
IOS().
Home().
SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithInterval(1), uixt.WithTapOffset(0, -50)).
SwipeToTapApp("$app_name", options.WithMaxRetryTimes(5), options.WithInterval(1), options.WithTapOffset(0, -50)).
Sleep(10),
hrp.NewStep("处理弹窗 close_popups 自定义配置 以及 ui_ocr exists 断言").
IOS().
ClosePopups(
uixt.WithMaxRetryTimes(3),
uixt.WithInterval(2),
options.WithMaxRetryTimes(3),
options.WithInterval(2),
).
Validate().
AssertOCRExists("推荐", "进入抖音失败"),
@@ -261,7 +260,7 @@ func TestIOSExpertTest(t *testing.T) {
Home().
AppTerminate("$bundle_id").
Sleep(3).
SwipeToTapApp("local", uixt.WithMaxRetryTimes(5)).Sleep(10),
SwipeToTapApp("local", options.WithMaxRetryTimes(5)).Sleep(10),
hrp.NewStep("screeshot 以及 sleep_random").
Loop(3).
IOS().

View File

@@ -4,7 +4,6 @@ import (
"testing"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -26,8 +25,8 @@ func TestHarmonyDouyinE2E(t *testing.T) {
Home().
SwipeToTapApp(
"抖音",
uixt.WithMaxRetryTimes(5),
uixt.WithTapOffset(0, -50),
options.WithMaxRetryTimes(5),
options.WithTapOffset(0, -50),
).
Sleep(20).
Validate().
@@ -48,11 +47,11 @@ func TestHarmonyDouyinE2E(t *testing.T) {
Harmony().
TapByOCR(
"直播中",
uixt.WithIgnoreNotFoundError(true),
uixt.WithIndex(-1),
options.WithIgnoreNotFoundError(true),
options.WithIndex(-1),
).
EndToEndDelay(uixt.WithInterval(5), uixt.WithTimeout(120)).
TapByUITypes(uixt.WithScreenShotUITypes("close")),
EndToEndDelay(options.WithInterval(5), options.WithTimeout(120)).
TapByUITypes(options.WithScreenShotUITypes("close")),
},
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/assert"
hrp "github.com/httprunner/httprunner/v5"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
@@ -67,24 +66,26 @@ func TestIOSDouyinWorldCupLive(t *testing.T) {
Home().
AppTerminate("$appBundleID"). // 关闭已运行的抖音
AppLaunch("$appBundleID").
TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)). // 处理青少年弹窗
TapByOCR("我知道了", options.WithIgnoreNotFoundError(true)). // 处理青少年弹窗
Validate().
AssertOCRExists("首页", "抖音启动失败,「首页」不存在"),
hrp.NewStep("点击首页").
IOS().
TapByOCR("首页", uixt.WithIndex(-1)).Sleep(2),
TapByOCR("首页", options.WithIndex(-1)).Sleep(2),
hrp.NewStep("点击世界杯页").
IOS().
SwipeToTapText("世界杯",
uixt.WithMaxRetryTimes(5),
uixt.WithCustomDirection(0.4, 0.07, 0.6, 0.07), // 滑动 tab从左到右解决「世界杯」被遮挡的问题
uixt.WithScope(0, 0, 1, 0.15), // 限定 tab 区域
uixt.WithInterval(1),
options.WithMaxRetryTimes(5),
options.WithCustomDirection(0.4, 0.07, 0.6, 0.07), // 滑动 tab从左到右解决「世界杯」被遮挡的问题
options.WithScope(0, 0, 1, 0.15), // 限定 tab 区域
options.WithInterval(1),
),
hrp.NewStep("点击进入赛程晋级").
Loop(5). // 重复执行 5 次
IOS().
TapByOCR("赛程晋级", uixt.WithIdentifier("click_live"), uixt.WithIndex(-1)).
TapByOCR("赛程晋级",
options.WithIdentifier("click_live"),
options.WithIndex(-1)).
Sleep(3).Back().Sleep(3),
hrp.NewStep("关闭抖音").
IOS().

View File

@@ -12,6 +12,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
type ActionMethod string
@@ -79,15 +80,15 @@ const (
)
type MobileAction struct {
Method ActionMethod `json:"method,omitempty" yaml:"method,omitempty"`
Params interface{} `json:"params,omitempty" yaml:"params,omitempty"`
Fn func() `json:"-" yaml:"-"` // only used for function action, not serialized
Options *ActionOptions `json:"options,omitempty" yaml:"options,omitempty"`
ActionOptions
Method ActionMethod `json:"method,omitempty" yaml:"method,omitempty"`
Params interface{} `json:"params,omitempty" yaml:"params,omitempty"`
Fn func() `json:"-" yaml:"-"` // only used for function action, not serialized
Options *options.ActionOptions `json:"options,omitempty" yaml:"options,omitempty"`
options.ActionOptions
}
func (ma MobileAction) GetOptions() []ActionOption {
var actionOptionList []ActionOption
func (ma MobileAction) GetOptions() []options.ActionOption {
var actionOptionList []options.ActionOption
// Notice: merge options from ma.Options and ma.ActionOptions
if ma.Options != nil {
actionOptionList = append(actionOptionList, ma.Options.Options()...)
@@ -96,457 +97,13 @@ func (ma MobileAction) GetOptions() []ActionOption {
return actionOptionList
}
// (x1, y1) is the top left corner, (x2, y2) is the bottom right corner
// [x1, y1, x2, y2] in percentage of the screen
type Scope []float64
// [x1, y1, x2, y2] in absolute pixels
type AbsScope []int
func (s AbsScope) Option() ActionOption {
return WithAbsScope(s[0], s[1], s[2], s[3])
}
type ActionOptions struct {
// log
Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // used to identify the action in log
// control related
MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times
IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found
Interval float64 `json:"interval,omitempty" yaml:"interval,omitempty"` // interval between retries in seconds
Duration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action
PressDuration float64 `json:"press_duration,omitempty" yaml:"press_duration,omitempty"` // used to set duration of ios swipe action
Steps int `json:"steps,omitempty" yaml:"steps,omitempty"` // used to set steps of android swipe action
Direction interface{} `json:"direction,omitempty" yaml:"direction,omitempty"` // used by swipe to tap text or app
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action
Frequency int `json:"frequency,omitempty" yaml:"frequency,omitempty"`
// scope related
Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"`
AbsScope AbsScope `json:"abs_scope,omitempty" yaml:"abs_scope,omitempty"`
Regex bool `json:"regex,omitempty" yaml:"regex,omitempty"` // use regex to match text
Offset []int `json:"offset,omitempty" yaml:"offset,omitempty"` // used to tap offset of point
OffsetRandomRange []int `json:"offset_random_range,omitempty" yaml:"offset_random_range,omitempty"` // set random range [min, max] for tap/swipe points
Index int `json:"index,omitempty" yaml:"index,omitempty"` // index of the target element
MatchOne bool `json:"match_one,omitempty" yaml:"match_one,omitempty"` // match one of the targets if existed
// set custiom options such as textview, id, description
Custom map[string]interface{} `json:"custom,omitempty" yaml:"custom,omitempty"`
// screenshot related
ScreenShotWithOCR bool `json:"screenshot_with_ocr,omitempty" yaml:"screenshot_with_ocr,omitempty"`
ScreenShotWithUpload bool `json:"screenshot_with_upload,omitempty" yaml:"screenshot_with_upload,omitempty"`
ScreenShotWithLiveType bool `json:"screenshot_with_live_type,omitempty" yaml:"screenshot_with_live_type,omitempty"`
ScreenShotWithLivePopularity bool `json:"screenshot_with_live_popularity,omitempty" yaml:"screenshot_with_live_popularity,omitempty"`
ScreenShotWithUITypes []string `json:"screenshot_with_ui_types,omitempty" yaml:"screenshot_with_ui_types,omitempty"`
ScreenShotWithClosePopups bool `json:"screenshot_with_close_popups,omitempty" yaml:"screenshot_with_close_popups,omitempty"`
ScreenShotWithOCRCluster string `json:"screenshot_with_ocr_cluster,omitempty" yaml:"screenshot_with_ocr_cluster,omitempty"`
ScreenShotFileName string `json:"screenshot_file_name,omitempty" yaml:"screenshot_file_name,omitempty"`
}
func (o *ActionOptions) Options() []ActionOption {
options := make([]ActionOption, 0)
if o == nil {
return options
}
if o.Identifier != "" {
options = append(options, WithIdentifier(o.Identifier))
}
if o.MaxRetryTimes != 0 {
options = append(options, WithMaxRetryTimes(o.MaxRetryTimes))
}
if o.IgnoreNotFoundError {
options = append(options, WithIgnoreNotFoundError(true))
}
if o.Interval != 0 {
options = append(options, WithInterval(o.Interval))
}
if o.Duration != 0 {
options = append(options, WithDuration(o.Duration))
}
if o.PressDuration != 0 {
options = append(options, WithPressDuration(o.PressDuration))
}
if o.Steps != 0 {
options = append(options, WithSteps(o.Steps))
}
switch v := o.Direction.(type) {
case string:
options = append(options, WithDirection(v))
case []float64:
options = append(options, WithCustomDirection(
v[0], v[1],
v[2], v[3],
))
case []interface{}:
// loaded from json case
// custom direction: [fromX, fromY, toX, toY]
sx, _ := builtin.Interface2Float64(v[0])
sy, _ := builtin.Interface2Float64(v[1])
ex, _ := builtin.Interface2Float64(v[2])
ey, _ := builtin.Interface2Float64(v[3])
options = append(options, WithCustomDirection(
sx, sy,
ex, ey,
))
}
if o.Timeout != 0 {
options = append(options, WithTimeout(o.Timeout))
}
if o.Frequency != 0 {
options = append(options, WithFrequency(o.Frequency))
}
if len(o.AbsScope) == 4 {
options = append(options, WithAbsScope(
o.AbsScope[0], o.AbsScope[1], o.AbsScope[2], o.AbsScope[3]))
} else if len(o.Scope) == 4 {
options = append(options, WithScope(
o.Scope[0], o.Scope[1], o.Scope[2], o.Scope[3]))
}
if len(o.Offset) == 2 {
// for tap [x,y] offset
options = append(options, WithTapOffset(o.Offset[0], o.Offset[1]))
} else if len(o.Offset) == 4 {
// for swipe [fromX, fromY, toX, toY] offset
options = append(options, WithSwipeOffset(
o.Offset[0], o.Offset[1], o.Offset[2], o.Offset[3]))
}
if len(o.OffsetRandomRange) == 2 {
options = append(options, WithOffsetRandomRange(
o.OffsetRandomRange[0], o.OffsetRandomRange[1]))
}
if o.Regex {
options = append(options, WithRegex(true))
}
if o.Index != 0 {
options = append(options, WithIndex(o.Index))
}
if o.MatchOne {
options = append(options, WithMatchOne(true))
}
// custom options
if o.Custom != nil {
for k, v := range o.Custom {
options = append(options, WithCustomOption(k, v))
}
}
// screenshot options
if o.ScreenShotWithOCR {
options = append(options, WithScreenShotOCR(true))
}
if o.ScreenShotWithUpload {
options = append(options, WithScreenShotUpload(true))
}
if o.ScreenShotWithLiveType {
options = append(options, WithScreenShotLiveType(true))
}
if o.ScreenShotWithLivePopularity {
options = append(options, WithScreenShotLivePopularity(true))
}
if len(o.ScreenShotWithUITypes) > 0 {
options = append(options, WithScreenShotUITypes(o.ScreenShotWithUITypes...))
}
if o.ScreenShotWithClosePopups {
options = append(options, WithScreenShotClosePopups(true))
}
if o.ScreenShotWithOCRCluster != "" {
options = append(options, WithScreenOCRCluster(o.ScreenShotWithOCRCluster))
}
if o.ScreenShotFileName != "" {
options = append(options, WithScreenShotFileName(o.ScreenShotFileName))
}
return options
}
func (o *ActionOptions) screenshotActions() []string {
actions := []string{}
if o.ScreenShotWithUpload {
actions = append(actions, "upload")
}
if o.ScreenShotWithOCR {
actions = append(actions, "ocr")
}
if o.ScreenShotWithLiveType {
actions = append(actions, "liveType")
}
if o.ScreenShotWithLivePopularity {
actions = append(actions, "livePopularity")
}
// UI detection
if len(o.ScreenShotWithUITypes) > 0 {
actions = append(actions, "ui")
}
if o.ScreenShotWithClosePopups {
actions = append(actions, "close")
}
return actions
}
func (o *ActionOptions) getRandomOffset() float64 {
if len(o.OffsetRandomRange) != 2 {
// invalid offset random range, should be [min, max]
return 0
}
minOffset := o.OffsetRandomRange[0]
maxOffset := o.OffsetRandomRange[1]
return float64(builtin.GetRandomNumber(minOffset, maxOffset)) + rand.Float64()
}
func (o *ActionOptions) updateData(data map[string]interface{}) {
if o.Identifier != "" {
data["log"] = map[string]interface{}{
"enable": true,
"data": o.Identifier,
}
}
if o.Steps > 0 {
data["steps"] = o.Steps
}
if _, ok := data["steps"]; !ok {
data["steps"] = 12 // default steps
}
if o.Duration > 0 {
data["duration"] = o.Duration
}
if _, ok := data["duration"]; !ok {
data["duration"] = 0 // default duration
}
if o.Frequency > 0 {
data["frequency"] = o.Frequency
}
if _, ok := data["frequency"]; !ok {
data["frequency"] = 10 // default frequency
}
if _, ok := data["replace"]; !ok {
data["replace"] = true // default true
}
// custom options
if o.Custom != nil {
for k, v := range o.Custom {
data[k] = v
}
}
}
func NewActionOptions(options ...ActionOption) *ActionOptions {
actionOptions := &ActionOptions{}
for _, option := range options {
option(actionOptions)
}
return actionOptions
}
type TapTextAction struct {
Text string
Options []ActionOption
Options []options.ActionOption
}
type ActionOption func(o *ActionOptions)
func WithCustomOption(key string, value interface{}) ActionOption {
return func(o *ActionOptions) {
if o.Custom == nil {
o.Custom = make(map[string]interface{})
}
o.Custom[key] = value
}
}
func WithIdentifier(identifier string) ActionOption {
return func(o *ActionOptions) {
o.Identifier = identifier
}
}
func WithIndex(index int) ActionOption {
return func(o *ActionOptions) {
o.Index = index
}
}
// set alias for compatibility
var WithWaitTime = WithInterval
func WithInterval(sec float64) ActionOption {
return func(o *ActionOptions) {
o.Interval = sec
}
}
func WithDuration(duration float64) ActionOption {
return func(o *ActionOptions) {
o.Duration = duration
}
}
func WithPressDuration(pressDuration float64) ActionOption {
return func(o *ActionOptions) {
o.PressDuration = pressDuration
}
}
func WithSteps(steps int) ActionOption {
return func(o *ActionOptions) {
o.Steps = steps
}
}
// WithDirection inputs direction (up, down, left, right)
func WithDirection(direction string) ActionOption {
return func(o *ActionOptions) {
o.Direction = direction
}
}
// WithCustomDirection inputs sx, sy, ex, ey
func WithCustomDirection(sx, sy, ex, ey float64) ActionOption {
return func(o *ActionOptions) {
o.Direction = []float64{sx, sy, ex, ey}
}
}
// WithScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all in [0, 1], which means the relative position of the screen
func WithScope(x1, y1, x2, y2 float64) ActionOption {
return func(o *ActionOptions) {
o.Scope = Scope{x1, y1, x2, y2}
}
}
// WithAbsScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all absolute position of the screen
func WithAbsScope(x1, y1, x2, y2 int) ActionOption {
return func(o *ActionOptions) {
o.AbsScope = AbsScope{x1, y1, x2, y2}
}
}
// Deprecated: use WithTapOffset instead
func WithOffset(offsetX, offsetY int) ActionOption {
return func(o *ActionOptions) {
o.Offset = []int{offsetX, offsetY}
}
}
// tap [x, y] with offset [offsetX, offsetY]
var WithTapOffset = WithOffset
// swipe [fromX, fromY, toX, toY] with offset [offsetFromX, offsetFromY, offsetToX, offsetToY]
func WithSwipeOffset(offsetFromX, offsetFromY, offsetToX, offsetToY int) ActionOption {
return func(o *ActionOptions) {
o.Offset = []int{offsetFromX, offsetFromY, offsetToX, offsetToY}
}
}
func WithOffsetRandomRange(min, max int) ActionOption {
return func(o *ActionOptions) {
o.OffsetRandomRange = []int{min, max}
}
}
func WithRegex(regex bool) ActionOption {
return func(o *ActionOptions) {
o.Regex = regex
}
}
func WithMatchOne(matchOne bool) ActionOption {
return func(o *ActionOptions) {
o.MatchOne = matchOne
}
}
func WithFrequency(frequency int) ActionOption {
return func(o *ActionOptions) {
o.Frequency = frequency
}
}
func WithMaxRetryTimes(maxRetryTimes int) ActionOption {
return func(o *ActionOptions) {
o.MaxRetryTimes = maxRetryTimes
}
}
func WithTimeout(timeout int) ActionOption {
return func(o *ActionOptions) {
o.Timeout = timeout
}
}
func WithIgnoreNotFoundError(ignoreError bool) ActionOption {
return func(o *ActionOptions) {
o.IgnoreNotFoundError = ignoreError
}
}
func WithScreenShotOCR(ocrOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithOCR = ocrOn
}
}
func WithScreenShotUpload(uploadOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithUpload = uploadOn
}
}
func WithScreenShotLiveType(liveTypeOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithLiveType = liveTypeOn
}
}
func WithScreenShotLivePopularity(livePopularityOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithLivePopularity = livePopularityOn
}
}
func WithScreenShotUITypes(uiTypes ...string) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithUITypes = uiTypes
}
}
func WithScreenShotClosePopups(closeOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithClosePopups = closeOn
}
}
func WithScreenOCRCluster(ocrCluster string) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithOCRCluster = ocrCluster
}
}
func WithScreenShotFileName(fileName string) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotFileName = fileName
}
}
func (dExt *DriverExt) ParseActionOptions(options ...ActionOption) []ActionOption {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) ParseActionOptions(opts ...options.ActionOption) []options.ActionOption {
actionOptions := options.NewActionOptions(opts...)
// convert relative scope to absolute scope
if len(actionOptions.AbsScope) != 4 && len(actionOptions.Scope) == 4 {
@@ -558,14 +115,14 @@ func (dExt *DriverExt) ParseActionOptions(options ...ActionOption) []ActionOptio
return actionOptions.Options()
}
func (dExt *DriverExt) GenAbsScope(x1, y1, x2, y2 float64) AbsScope {
func (dExt *DriverExt) GenAbsScope(x1, y1, x2, y2 float64) options.AbsScope {
// convert relative scope to absolute scope
windowSize, _ := dExt.Driver.WindowSize()
absX1 := int(x1 * float64(windowSize.Width))
absY1 := int(y1 * float64(windowSize.Height))
absX2 := int(x2 * float64(windowSize.Width))
absY2 := int(y2 * float64(windowSize.Height))
return AbsScope{absX1, absY1, absX2, absY2}
return options.AbsScope{absX1, absY1, absX2, absY2}
}
func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
@@ -700,7 +257,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapByOCR, action.Params)
case ACTION_TapByCV:
actionOptions := NewActionOptions(action.GetOptions()...)
actionOptions := options.NewActionOptions(action.GetOptions()...)
if len(actionOptions.ScreenShotWithUITypes) > 0 {
return dExt.TapByUIDetection(action.GetOptions()...)
}

View File

@@ -11,11 +11,12 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
type IImageService interface {
// GetImage returns image result including ocr texts, uploaded image url, etc
GetImage(imageBuf *bytes.Buffer, options ...ActionOption) (imageResult *ImageResult, err error)
GetImage(imageBuf *bytes.Buffer, opts ...options.ActionOption) (imageResult *ImageResult, err error)
}
type ImageResult struct {
@@ -105,7 +106,7 @@ func (t OCRTexts) texts() (texts []string) {
return texts
}
func (t OCRTexts) FilterScope(scope AbsScope) (results OCRTexts) {
func (t OCRTexts) FilterScope(scope options.AbsScope) (results OCRTexts) {
for _, ocrText := range t {
rect := ocrText.Rect
@@ -127,8 +128,8 @@ func (t OCRTexts) FilterScope(scope AbsScope) (results OCRTexts) {
// FindText returns matched text with options
// Notice: filter scope should be specified with WithAbsScope
func (t OCRTexts) FindText(text string, options ...ActionOption) (result OCRText, err error) {
actionOptions := NewActionOptions(options...)
func (t OCRTexts) FindText(text string, opts ...options.ActionOption) (result OCRText, err error) {
actionOptions := options.NewActionOptions(opts...)
var results []OCRText
for _, ocrText := range t.FilterScope(actionOptions.AbsScope) {
@@ -172,10 +173,10 @@ func (t OCRTexts) FindText(text string, options ...ActionOption) (result OCRText
return results[idx], nil
}
func (t OCRTexts) FindTexts(texts []string, options ...ActionOption) (results OCRTexts, err error) {
actionOptions := NewActionOptions(options...)
func (t OCRTexts) FindTexts(texts []string, opts ...options.ActionOption) (results OCRTexts, err error) {
actionOptions := options.NewActionOptions(opts...)
for _, text := range texts {
ocrText, err := t.FindText(text, options...)
ocrText, err := t.FindText(text, opts...)
if err != nil {
continue
}
@@ -239,7 +240,7 @@ func (box Box) Center() PointF {
type UIResults []UIResult
func (u UIResults) FilterScope(scope AbsScope) (results UIResults) {
func (u UIResults) FilterScope(scope options.AbsScope) (results UIResults) {
for _, uiResult := range u {
rect := image.Rectangle{
Min: image.Point{
@@ -267,8 +268,8 @@ func (u UIResults) FilterScope(scope AbsScope) (results UIResults) {
return
}
func (u UIResults) GetUIResult(options ...ActionOption) (UIResult, error) {
actionOptions := NewActionOptions(options...)
func (u UIResults) GetUIResult(opts ...options.ActionOption) (UIResult, error) {
actionOptions := options.NewActionOptions(opts...)
uiResults := u.FilterScope(actionOptions.AbsScope)
if len(uiResults) == 0 {

View File

@@ -16,6 +16,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
var client = &http.Client{
@@ -46,9 +47,9 @@ func newVEDEMImageService() (*veDEMImageService, error) {
// ui - get ui position by type(s)
type veDEMImageService struct{}
func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, options ...ActionOption) (imageResult *ImageResult, err error) {
actionOptions := NewActionOptions(options...)
screenshotActions := actionOptions.screenshotActions()
func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, opts ...options.ActionOption) (imageResult *ImageResult, err error) {
actionOptions := options.NewActionOptions(opts...)
screenshotActions := actionOptions.ScreenshotActions()
if len(screenshotActions) == 0 {
// skip
return nil, nil

View File

@@ -62,7 +62,7 @@ func TestTapUIWithScreenshot(t *testing.T) {
}
err = driver.TapByUIDetection(
WithScreenShotUITypes("dyhouse", "shoppingbag"))
options.WithScreenShotUITypes("dyhouse", "shoppingbag"))
if err != nil {
t.Fatal(err)
}

View File

@@ -301,20 +301,20 @@ func (dev *AndroidDevice) Uninstall(packageName string) error {
return err
}
func (dev *AndroidDevice) Install(apkPath string, options ...InstallOption) error {
opts := NewInstallOptions(options...)
func (dev *AndroidDevice) Install(apkPath string, opts ...InstallOption) error {
installOpts := NewInstallOptions(opts...)
brand, err := dev.d.Brand()
if err != nil {
return err
}
args := []string{}
if opts.Reinstall {
if installOpts.Reinstall {
args = append(args, "-r")
}
if opts.GrantPermission {
if installOpts.GrantPermission {
args = append(args, "-g")
}
if opts.Downgrade {
if installOpts.Downgrade {
args = append(args, "-d")
}
switch strings.ToLower(brand) {

View File

@@ -193,7 +193,7 @@ func (ad *adbDriver) GetTimestamp() (timestamp int64, err error) {
}
// PressBack simulates a short press on the BACK button.
func (ad *adbDriver) PressBack(options ...ActionOption) (err error) {
func (ad *adbDriver) PressBack(opts ...options.ActionOption) (err error) {
// adb shell input keyevent 4
_, err = ad.runShellCommand("input", "keyevent", fmt.Sprintf("%d", KCBack))
if err != nil {
@@ -294,7 +294,7 @@ func (ad *adbDriver) Unlock() (err error) {
return ad.Swipe(500, 1500, 500, 500)
}
func (ad *adbDriver) Backspace(count int, options ...ActionOption) (err error) {
func (ad *adbDriver) Backspace(count int, opts ...options.ActionOption) (err error) {
if count == 0 {
return nil
}
@@ -370,15 +370,15 @@ func (ad *adbDriver) AppTerminate(packageName string) (successful bool, err erro
return true, nil
}
func (ad *adbDriver) Tap(x, y float64, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) Tap(x, y float64, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 2 {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
x += actionOptions.GetRandomOffset()
y += actionOptions.GetRandomOffset()
// adb shell input tap x y
xStr := fmt.Sprintf("%.1f", x)
@@ -391,7 +391,7 @@ func (ad *adbDriver) Tap(x, y float64, options ...ActionOption) error {
return nil
}
func (ad *adbDriver) DoubleTap(x, y float64, options ...ActionOption) error {
func (ad *adbDriver) DoubleTap(x, y float64, opts ...options.ActionOption) error {
// adb shell input tap x y
xStr := fmt.Sprintf("%.1f", x)
yStr := fmt.Sprintf("%.1f", y)
@@ -409,15 +409,15 @@ func (ad *adbDriver) DoubleTap(x, y float64, options ...ActionOption) error {
return nil
}
func (ad *adbDriver) TouchAndHold(x, y float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) TouchAndHold(x, y float64, opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 2 {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
x += actionOptions.GetRandomOffset()
y += actionOptions.GetRandomOffset()
duration := 1000.0
if actionOptions.Duration > 0 {
duration = actionOptions.Duration * 1000
@@ -435,8 +435,8 @@ func (ad *adbDriver) TouchAndHold(x, y float64, options ...ActionOption) (err er
return nil
}
func (ad *adbDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
@@ -444,10 +444,10 @@ func (ad *adbDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOptio
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
duration := 200.0
if actionOptions.Duration > 0 {
duration = actionOptions.Duration * 1000
@@ -469,8 +469,8 @@ func (ad *adbDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOptio
return nil
}
func (ad *adbDriver) Swipe(fromX, fromY, toX, toY float64, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) Swipe(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
@@ -478,10 +478,10 @@ func (ad *adbDriver) Swipe(fromX, fromY, toX, toY float64, options ...ActionOpti
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
// adb shell input swipe fromX fromY toX toY
_, err := ad.runShellCommand(
@@ -514,16 +514,16 @@ func (ad *adbDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffe
return
}
func (ad *adbDriver) SendKeys(text string, options ...ActionOption) (err error) {
err = ad.SendUnicodeKeys(text, options...)
func (ad *adbDriver) SendKeys(text string, opts ...options.ActionOption) (err error) {
err = ad.SendUnicodeKeys(text, opts...)
if err == nil {
return
}
err = ad.InputText(text, options...)
err = ad.InputText(text, opts...)
return
}
func (ad *adbDriver) InputText(text string, options ...ActionOption) error {
func (ad *adbDriver) InputText(text string, opts ...options.ActionOption) error {
// adb shell input text <text>
_, err := ad.runShellCommand("input", "text", text)
if err != nil {
@@ -532,7 +532,7 @@ func (ad *adbDriver) InputText(text string, options ...ActionOption) error {
return nil
}
func (ad *adbDriver) SendUnicodeKeys(text string, options ...ActionOption) (err error) {
func (ad *adbDriver) SendUnicodeKeys(text string, opts ...options.ActionOption) (err error) {
// If the Unicode IME is not installed, fall back to the old interface.
// There might be differences in the tracking schemes across different phones, and it is pending further verification.
// In release version: without the Unicode IME installed, the test cannot execute.
@@ -558,7 +558,7 @@ func (ad *adbDriver) SendUnicodeKeys(text string, options ...ActionOption) (err
log.Warn().Err(err).Msgf("encode text with modified utf7 failed")
return
}
err = ad.InputText("\""+strings.ReplaceAll(encodedStr, "\"", "\\\"")+"\"", options...)
err = ad.InputText("\""+strings.ReplaceAll(encodedStr, "\"", "\\\"")+"\"", opts...)
return
}
@@ -619,8 +619,8 @@ func (ad *adbDriver) SendKeysByAdbKeyBoard(text string) (err error) {
return
}
func (ad *adbDriver) Input(text string, options ...ActionOption) (err error) {
return ad.SendKeys(text, options...)
func (ad *adbDriver) Input(text string, opts ...options.ActionOption) (err error) {
return ad.SendKeys(text, opts...)
}
func (ad *adbDriver) Clear(packageName string) error {
@@ -694,17 +694,17 @@ func (ad *adbDriver) sourceTree(srcOpt ...SourceOption) (sourceTree *Hierarchy,
return
}
func (ad *adbDriver) TapByText(text string, options ...ActionOption) error {
func (ad *adbDriver) TapByText(text string, opts ...options.ActionOption) error {
sourceTree, err := ad.sourceTree()
if err != nil {
return err
}
return ad.tapByTextUsingHierarchy(sourceTree, text, options...)
return ad.tapByTextUsingHierarchy(sourceTree, text, opts...)
}
func (ad *adbDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string, options ...ActionOption) error {
bounds := ad.searchNodes(hierarchy.Layout, text, options...)
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string, opts ...options.ActionOption) error {
bounds := ad.searchNodes(hierarchy.Layout, text, opts...)
actionOptions := options.NewActionOptions(opts...)
if len(bounds) == 0 {
if actionOptions.IgnoreNotFoundError {
log.Info().Msg("not found element by text " + text)
@@ -714,7 +714,7 @@ func (ad *adbDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string,
}
for _, bound := range bounds {
width, height := bound.Center()
err := ad.Tap(width, height, options...)
err := ad.Tap(width, height, opts...)
if err != nil {
return err
}
@@ -737,11 +737,11 @@ func (ad *adbDriver) TapByTexts(actions ...TapTextAction) error {
return nil
}
func (ad *adbDriver) searchNodes(nodes []Layout, text string, options ...ActionOption) []Bounds {
actionOptions := NewActionOptions(options...)
func (ad *adbDriver) searchNodes(nodes []Layout, text string, opts ...options.ActionOption) []Bounds {
actionOptions := options.NewActionOptions(opts...)
var results []Bounds
for _, node := range nodes {
result := ad.searchNodes(node.Layout, text, options...)
result := ad.searchNodes(node.Layout, text, opts...)
results = append(results, result...)
if actionOptions.Regex {
// regex on, check if match regex
@@ -751,7 +751,7 @@ func (ad *adbDriver) searchNodes(nodes []Layout, text string, options ...ActionO
} else {
// regex off, check if match exactly
if node.Text != text {
ad.searchNodes(node.Layout, text, options...)
ad.searchNodes(node.Layout, text, opts...)
continue
}
}

View File

@@ -79,7 +79,8 @@ func TestDoubleTap(t *testing.T) {
func TestLongPress(t *testing.T) {
setupStubDriver(t)
err := androidStubDriver.Swipe(1036, 1076, 1036, 1076, WithDuration(3))
err := androidStubDriver.Swipe(1036, 1076, 1036, 1076,
options.WithDuration(3))
if err != nil {
t.Fatal(err)
}

View File

@@ -250,7 +250,7 @@ func (ud *uiaDriver) WindowSize() (size Size, err error) {
}
// PressBack simulates a short press on the BACK button.
func (ud *uiaDriver) PressBack(options ...ActionOption) (err error) {
func (ud *uiaDriver) PressBack(opts ...options.ActionOption) (err error) {
// register(postHandler, new PressBack("/wd/hub/session/:sessionId/back"))
_, err = ud.httpPOST(nil, "/session", ud.session.ID, "back")
return
@@ -293,7 +293,7 @@ func (ud *uiaDriver) Orientation() (orientation Orientation, err error) {
return
}
func (ud *uiaDriver) DoubleTap(x, y float64, options ...ActionOption) error {
func (ud *uiaDriver) DoubleTap(x, y float64, opts ...options.ActionOption) error {
return ud.DoubleFloatTap(x, y)
}
@@ -319,16 +319,16 @@ func (ud *uiaDriver) DoubleFloatTap(x, y float64) error {
return err
}
func (ud *uiaDriver) Tap(x, y float64, options ...ActionOption) (err error) {
func (ud *uiaDriver) Tap(x, y float64, opts ...options.ActionOption) (err error) {
// register(postHandler, new Tap("/wd/hub/session/:sessionId/appium/tap"))
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 2 {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
x += actionOptions.GetRandomOffset()
y += actionOptions.GetRandomOffset()
duration := 100.0
if actionOptions.PressDuration > 0 {
@@ -351,15 +351,15 @@ func (ud *uiaDriver) Tap(x, y float64, options ...ActionOption) (err error) {
}
// update data options in post data for extra uiautomator configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = ud.httpPOST(data, "/session", ud.session.ID, "actions/tap")
return err
}
func (ud *uiaDriver) TouchAndHold(x, y float64, options ...ActionOption) (err error) {
opts := NewActionOptions(options...)
duration := opts.Duration
func (ud *uiaDriver) TouchAndHold(x, y float64, opts ...options.ActionOption) (err error) {
actionOpts := options.NewActionOptions(opts...)
duration := actionOpts.Duration
if duration == 0 {
duration = 1.0
}
@@ -379,18 +379,18 @@ func (ud *uiaDriver) TouchAndHold(x, y float64, options ...ActionOption) (err er
// the smoothness and speed of the swipe by specifying the number of steps.
// Each step execution is throttled to 5 milliseconds per step, so for a 100
// steps, the swipe will take around 0.5 seconds to complete.
func (ud *uiaDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (ud *uiaDriver) Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
fromY += float64(actionOptions.Offset[1])
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
data := map[string]interface{}{
"startX": fromX,
@@ -400,7 +400,7 @@ func (ud *uiaDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOptio
}
// update data options in post data for extra uiautomator configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
// register(postHandler, new Drag("/wd/hub/session/:sessionId/touch/drag"))
_, err = ud.httpPOST(data, "/session", ud.session.ID, "touch/drag")
@@ -412,19 +412,19 @@ func (ud *uiaDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOptio
// per step. So for a 100 steps, the swipe will take about 1/2 second to complete.
//
// `steps` is the number of move steps sent to the system
func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY float64, options ...ActionOption) error {
func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
// register(postHandler, new Swipe("/wd/hub/session/:sessionId/touch/perform"))
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
fromY += float64(actionOptions.Offset[1])
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
duration := 200.0
if actionOptions.PressDuration > 0 {
@@ -447,7 +447,7 @@ func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY float64, options ...ActionOpti
}
// update data options in post data for extra uiautomator configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err := ud.httpPOST(data, "/session", ud.session.ID, "actions/swipe")
return err
@@ -497,25 +497,25 @@ func (ud *uiaDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffe
}
// SendKeys Android input does not support setting frequency.
func (ud *uiaDriver) SendKeys(text string, options ...ActionOption) (err error) {
func (ud *uiaDriver) SendKeys(text string, opts ...options.ActionOption) (err error) {
// register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/keys"))
// https://github.com/appium/appium-uiautomator2-server/blob/master/app/src/main/java/io/appium/uiautomator2/handler/SendKeysToElement.java#L76-L85
actionOptions := NewActionOptions(options...)
err = ud.SendUnicodeKeys(text, options...)
actionOptions := options.NewActionOptions(opts...)
err = ud.SendUnicodeKeys(text, opts...)
if err != nil {
data := map[string]interface{}{
"text": text,
}
// new data options in post data for extra uiautomator configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = ud.httpPOST(data, "/session", ud.session.ID, "/keys")
}
return
}
func (ud *uiaDriver) SendUnicodeKeys(text string, options ...ActionOption) (err error) {
func (ud *uiaDriver) SendUnicodeKeys(text string, opts ...options.ActionOption) (err error) {
// If the Unicode IME is not installed, fall back to the old interface.
// There might be differences in the tracking schemes across different phones, and it is pending further verification.
// In release version: without the Unicode IME installed, the test cannot execute.
@@ -541,12 +541,12 @@ func (ud *uiaDriver) SendUnicodeKeys(text string, options ...ActionOption) (err
log.Warn().Err(err).Msgf("encode text with modified utf7 failed")
return
}
err = ud.SendActionKey(encodedStr, options...)
err = ud.SendActionKey(encodedStr, opts...)
return
}
func (ud *uiaDriver) SendActionKey(text string, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (ud *uiaDriver) SendActionKey(text string, opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
var actions []interface{}
for i, c := range text {
actions = append(actions, map[string]interface{}{"type": "keyDown", "value": string(c)},
@@ -567,13 +567,13 @@ func (ud *uiaDriver) SendActionKey(text string, options ...ActionOption) (err er
}
// new data options in post data for extra uiautomator configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = ud.httpPOST(data, "/session", ud.session.ID, "/actions/keys")
return
}
func (ud *uiaDriver) Input(text string, options ...ActionOption) (err error) {
return ud.SendKeys(text, options...)
func (ud *uiaDriver) Input(text string, opts ...options.ActionOption) (err error) {
return ud.SendKeys(text, opts...)
}
func (ud *uiaDriver) Rotation() (rotation Rotation, err error) {
@@ -625,12 +625,12 @@ func (ud *uiaDriver) sourceTree(srcOpt ...SourceOption) (sourceTree *Hierarchy,
return
}
func (ud *uiaDriver) TapByText(text string, options ...ActionOption) error {
func (ud *uiaDriver) TapByText(text string, opts ...options.ActionOption) error {
sourceTree, err := ud.sourceTree()
if err != nil {
return err
}
return ud.tapByTextUsingHierarchy(sourceTree, text, options...)
return ud.tapByTextUsingHierarchy(sourceTree, text, opts...)
}
func (ud *uiaDriver) TapByTexts(actions ...TapTextAction) error {

View File

@@ -220,7 +220,9 @@ func TestDriver_DeviceInfo(t *testing.T) {
func TestDriver_Tap(t *testing.T) {
setupAndroidUIA2Driver(t)
driverExt.Driver.StartCaptureLog("")
err := driverExt.TapXY(0.5, 0.5, WithIdentifier("test"), WithPressDuration(4))
err := driverExt.TapXY(0.5, 0.5,
options.WithIdentifier("test"),
options.WithPressDuration(4))
if err != nil {
t.Fatal(err)
}
@@ -237,7 +239,8 @@ func TestDriver_Tap(t *testing.T) {
func TestDriver_Swipe(t *testing.T) {
setupAndroidUIA2Driver(t)
err := driverExt.Driver.Swipe(400, 1000, 400, 500, WithPressDuration(0.5))
err := driverExt.Driver.Swipe(400, 1000, 400, 500,
options.WithPressDuration(0.5))
if err != nil {
t.Fatal(err)
}
@@ -273,7 +276,8 @@ func TestDriver_Drag(t *testing.T) {
func TestDriver_SendKeys(t *testing.T) {
setupAndroidUIA2Driver(t)
err := driverExt.Driver.SendKeys("辽宁省沈阳市新民市民族街36-4", WithIdentifier("test"))
err := driverExt.Driver.SendKeys("辽宁省沈阳市新民市民族街36-4",
options.WithIdentifier("test"))
if err != nil {
t.Fatal(err)
}
@@ -494,9 +498,29 @@ func TestDriver_ShellInputUnicode(t *testing.T) {
func TestTapTexts(t *testing.T) {
setupAndroidUIA2Driver(t)
actions := []TapTextAction{
{Text: "^.*无视风险安装$", Options: []ActionOption{WithTapOffset(100, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
{Text: "已了解此应用未经检测.*", Options: []ActionOption{WithTapOffset(-450, 0), WithRegex(true), WithIgnoreNotFoundError(true)}},
{Text: "^(.*无视风险安装|确定|继续|完成|点击继续安装|继续安装旧版本|替换|安装|授权本次安装|继续安装|重新安装)$", Options: []ActionOption{WithRegex(true), WithIgnoreNotFoundError(true)}},
{
Text: "^.*无视风险安装$",
Options: []options.ActionOption{
options.WithTapOffset(100, 0),
options.WithRegex(true),
options.WithIgnoreNotFoundError(true),
},
},
{
Text: "已了解此应用未经检测.*",
Options: []options.ActionOption{
options.WithTapOffset(-450, 0),
options.WithRegex(true),
options.WithIgnoreNotFoundError(true),
},
},
{
Text: "^(.*无视风险安装|确定|继续|完成|点击继续安装|继续安装旧版本|替换|安装|授权本次安装|继续安装|重新安装)$",
Options: []options.ActionOption{
options.WithRegex(true),
options.WithIgnoreNotFoundError(true),
},
},
}
err := driverExt.Driver.TapByTexts(actions...)
if err != nil {

View File

@@ -63,29 +63,29 @@ func (dExt *DriverExt) Init() error {
}
func (dExt *DriverExt) assertOCR(text, assert string) error {
var options []ActionOption
options = append(options, WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text)))
var opts []options.ActionOption
opts = append(opts, options.WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text)))
switch assert {
case AssertionEqual:
_, err := dExt.FindScreenText(text, options...)
_, err := dExt.FindScreenText(text, opts...)
if err != nil {
return errors.Wrap(err, "assert ocr equal failed")
}
case AssertionNotEqual:
_, err := dExt.FindScreenText(text, options...)
_, err := dExt.FindScreenText(text, opts...)
if err == nil {
return errors.New("assert ocr not equal failed")
}
case AssertionExists:
options = append(options, WithRegex(true))
_, err := dExt.FindScreenText(text, options...)
opts = append(opts, options.WithRegex(true))
_, err := dExt.FindScreenText(text, opts...)
if err != nil {
return errors.Wrap(err, "assert ocr exists failed")
}
case AssertionNotExists:
options = append(options, WithRegex(true))
_, err := dExt.FindScreenText(text, options...)
opts = append(opts, options.WithRegex(true))
_, err := dExt.FindScreenText(text, opts...)
if err == nil {
return errors.New("assert ocr not exists failed")
}

View File

@@ -120,7 +120,7 @@ func (dev *HarmonyDevice) NewUSBDriver(opts ...options.DriverOption) (driver IWe
return harmonyDriver, nil
}
func (dev *HarmonyDevice) Install(appPath string, options ...InstallOption) error {
func (dev *HarmonyDevice) Install(appPath string, opts ...InstallOption) error {
return nil
}

View File

@@ -155,16 +155,16 @@ func (hd *hdcDriver) Orientation() (orientation Orientation, err error) {
return OrientationPortrait, nil
}
func (hd *hdcDriver) Tap(x, y float64, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (hd *hdcDriver) Tap(x, y float64, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 2 {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
x += actionOptions.GetRandomOffset()
y += actionOptions.GetRandomOffset()
if actionOptions.Identifier != "" {
startTime := int(time.Now().UnixMilli())
hd.points = append(hd.points, ExportPoint{Start: startTime, End: startTime + 100, Ext: actionOptions.Identifier, RunTime: 100})
@@ -172,31 +172,31 @@ func (hd *hdcDriver) Tap(x, y float64, options ...ActionOption) error {
return hd.uiDriver.InjectGesture(ghdc.NewGesture().Start(ghdc.Point{X: int(x), Y: int(y)}).Pause(100))
}
func (hd *hdcDriver) DoubleTap(x, y float64, options ...ActionOption) error {
func (hd *hdcDriver) DoubleTap(x, y float64, opts ...options.ActionOption) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) TouchAndHold(x, y float64, options ...ActionOption) (err error) {
func (hd *hdcDriver) TouchAndHold(x, y float64, opts ...options.ActionOption) (err error) {
return errDriverNotImplemented
}
func (hd *hdcDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) error {
func (hd *hdcDriver) Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
return errDriverNotImplemented
}
// Swipe works like Drag, but `pressForDuration` value is 0
func (hd *hdcDriver) Swipe(fromX, fromY, toX, toY float64, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (hd *hdcDriver) Swipe(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
fromY += float64(actionOptions.Offset[1])
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
duration := 200
if actionOptions.PressDuration > 0 {
@@ -221,11 +221,11 @@ func (hd *hdcDriver) SetIme(ime string) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) SendKeys(text string, options ...ActionOption) error {
func (hd *hdcDriver) SendKeys(text string, opts ...options.ActionOption) error {
return hd.uiDriver.InputText(text)
}
func (hd *hdcDriver) Input(text string, options ...ActionOption) error {
func (hd *hdcDriver) Input(text string, opts ...options.ActionOption) error {
return hd.uiDriver.InputText(text)
}
@@ -237,11 +237,11 @@ func (hd *hdcDriver) PressButton(devBtn DeviceButton) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) PressBack(options ...ActionOption) error {
func (hd *hdcDriver) PressBack(opts ...options.ActionOption) error {
return hd.uiDriver.PressBack()
}
func (hd *hdcDriver) Backspace(count int, options ...ActionOption) (err error) {
func (hd *hdcDriver) Backspace(count int, opts ...options.ActionOption) (err error) {
return nil
}
@@ -286,7 +286,7 @@ func (hd *hdcDriver) LogoutNoneUI(packageName string) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) TapByText(text string, options ...ActionOption) error {
func (hd *hdcDriver) TapByText(text string, opts ...options.ActionOption) error {
return errDriverNotImplemented
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
type InstallOptions struct {
@@ -20,9 +21,9 @@ type InstallOptions struct {
type InstallOption func(o *InstallOptions)
func NewInstallOptions(options ...InstallOption) *InstallOptions {
func NewInstallOptions(opts ...InstallOption) *InstallOptions {
installOptions := &InstallOptions{}
for _, option := range options {
for _, option := range opts {
option(installOptions)
}
return installOptions
@@ -58,7 +59,7 @@ type InstallResult struct {
ErrorMsg string `json:"errorMsg"`
}
func (dExt *DriverExt) InstallByUrl(url string, options ...InstallOption) error {
func (dExt *DriverExt) InstallByUrl(url string, opts ...InstallOption) error {
// 获取当前目录
cwd, err := os.Getwd()
if err != nil {
@@ -73,7 +74,7 @@ func (dExt *DriverExt) InstallByUrl(url string, options ...InstallOption) error
return err
}
err = dExt.Install(appPath, options...)
err = dExt.Install(appPath, opts...)
if err != nil {
log.Error().Err(err).Msg("install app failed")
return err
@@ -81,7 +82,7 @@ func (dExt *DriverExt) InstallByUrl(url string, options ...InstallOption) error
return nil
}
func (dExt *DriverExt) Install(filePath string, options ...InstallOption) error {
func (dExt *DriverExt) Install(filePath string, opts ...InstallOption) error {
if _, ok := dExt.Device.(*AndroidDevice); ok {
stopChan := make(chan struct{})
go func() {
@@ -93,19 +94,28 @@ func (dExt *DriverExt) Install(filePath string, options ...InstallOption) error
case <-ticker.C:
actions := []TapTextAction{
{
Text: "^.*无视风险安装$",
Options: []ActionOption{WithTapOffset(100, 0), WithRegex(true), WithIgnoreNotFoundError(true)},
Text: "^.*无视风险安装$",
Options: []options.ActionOption{
options.WithTapOffset(100, 0),
options.WithRegex(true),
options.WithIgnoreNotFoundError(true),
},
},
{
Text: "^已了解此应用未经检测.*",
Options: []ActionOption{WithTapOffset(-450, 0), WithRegex(true), WithIgnoreNotFoundError(true)},
Text: "^已了解此应用未经检测.*",
Options: []options.ActionOption{
options.WithTapOffset(-450, 0),
options.WithRegex(true),
options.WithIgnoreNotFoundError(true),
},
},
}
_ = dExt.Driver.TapByTexts(actions...)
_ = dExt.TapByOCR(
"^(.*无视风险安装|确定|继续|完成|点击继续安装|继续安装旧版本|替换|授权本次安装|稍后提醒|继续安装|重新安装|安装)$",
WithRegex(true), WithIgnoreNotFoundError(true),
options.WithRegex(true),
options.WithIgnoreNotFoundError(true),
)
case <-stopChan:
log.Info().Msg("Ticker stopped")
@@ -118,11 +128,11 @@ func (dExt *DriverExt) Install(filePath string, options ...InstallOption) error
}()
}
return dExt.Device.Install(filePath, options...)
return dExt.Device.Install(filePath, opts...)
}
func (dExt *DriverExt) Uninstall(packageName string, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) Uninstall(packageName string, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
err := dExt.Device.Uninstall(packageName)
if err != nil {
log.Warn().Err(err).Msg("failed to uninstall")

View File

@@ -359,7 +359,7 @@ type IDevice interface {
// TODO: add ctx to NewDriver
NewDriver(...options.DriverOption) (driverExt *DriverExt, err error)
Install(appPath string, options ...InstallOption) error
Install(appPath string, opts ...InstallOption) error
Uninstall(packageName string) error
GetPackageInfo(packageName string) (AppInfo, error)
@@ -438,21 +438,21 @@ type IWebDriver interface {
Orientation() (orientation Orientation, err error)
// Tap Sends a tap event at the coordinate.
Tap(x, y float64, options ...ActionOption) error
Tap(x, y float64, opts ...options.ActionOption) error
// DoubleTap Sends a double tap event at the coordinate.
DoubleTap(x, y float64, options ...ActionOption) error
DoubleTap(x, y float64, opts ...options.ActionOption) error
// TouchAndHold Initiates a long-press gesture at the coordinate, holding for the specified duration.
// second: The default value is 1
TouchAndHold(x, y float64, options ...ActionOption) error
TouchAndHold(x, y float64, opts ...options.ActionOption) error
// Drag Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
// WithPressDurationOption option can be used to set pressForDuration (default to 1 second).
Drag(fromX, fromY, toX, toY float64, options ...ActionOption) error
Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error
// Swipe works like Drag, but `pressForDuration` value is 0
Swipe(fromX, fromY, toX, toY float64, options ...ActionOption) error
Swipe(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error
// SetPasteboard Sets data to the general pasteboard
SetPasteboard(contentType PasteboardType, content string) error
@@ -465,10 +465,10 @@ type IWebDriver interface {
// SendKeys Types a string into active element. There must be element with keyboard focus,
// otherwise an error is raised.
// WithFrequency option can be used to set frequency of typing (letters per sec). The default value is 60
SendKeys(text string, options ...ActionOption) error
SendKeys(text string, opts ...options.ActionOption) error
// Input works like SendKeys
Input(text string, options ...ActionOption) error
Input(text string, opts ...options.ActionOption) error
Clear(packageName string) error
@@ -476,11 +476,11 @@ type IWebDriver interface {
PressButton(devBtn DeviceButton) error
// PressBack Presses the back button
PressBack(options ...ActionOption) error
PressBack(opts ...options.ActionOption) error
PressKeyCode(keyCode KeyCode) (err error)
Backspace(count int, options ...ActionOption) (err error)
Backspace(count int, opts ...options.ActionOption) (err error)
Screenshot() (*bytes.Buffer, error)
@@ -490,7 +490,7 @@ type IWebDriver interface {
LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error)
LogoutNoneUI(packageName string) error
TapByText(text string, options ...ActionOption) error
TapByText(text string, opts ...options.ActionOption) error
TapByTexts(actions ...TapTextAction) error
// AccessibleSource Return application elements accessibility tree

View File

@@ -323,9 +323,9 @@ func (dev *IOSDevice) NewDriver(opts ...options.DriverOption) (driverExt *Driver
return driverExt, nil
}
func (dev *IOSDevice) Install(appPath string, options ...InstallOption) (err error) {
opts := NewInstallOptions(options...)
for i := 0; i <= opts.RetryTimes; i++ {
func (dev *IOSDevice) Install(appPath string, opts ...InstallOption) (err error) {
installOpts := NewInstallOptions(opts...)
for i := 0; i <= installOpts.RetryTimes; i++ {
var conn *zipconduit.Connection
conn, err = zipconduit.New(dev.d)
if err != nil {
@@ -343,12 +343,12 @@ func (dev *IOSDevice) Install(appPath string, options ...InstallOption) (err err
return err
}
func (dev *IOSDevice) InstallByUrl(url string, options ...InstallOption) (err error) {
func (dev *IOSDevice) InstallByUrl(url string, opts ...InstallOption) (err error) {
appPath, err := builtin.DownloadFileByUrl(url)
if err != nil {
return err
}
err = dev.Install(appPath, options...)
err = dev.Install(appPath, opts...)
if err != nil {
return err
}

View File

@@ -220,42 +220,42 @@ func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) {
}
// Tap Sends a tap event at the coordinate.
func (s *stubIOSDriver) Tap(x, y float64, options ...ActionOption) error {
func (s *stubIOSDriver) Tap(x, y float64, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.Tap(x, y, options...)
return s.wdaDriver.Tap(x, y, opts...)
}
// DoubleTap Sends a double tap event at the coordinate.
func (s *stubIOSDriver) DoubleTap(x, y float64, options ...ActionOption) error {
func (s *stubIOSDriver) DoubleTap(x, y float64, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.DoubleTap(x, y, options...)
return s.wdaDriver.DoubleTap(x, y, opts...)
}
// TouchAndHold Initiates a long-press gesture at the coordinate, holding for the specified duration.
//
// second: The default value is 1
func (s *stubIOSDriver) TouchAndHold(x, y float64, options ...ActionOption) error {
func (s *stubIOSDriver) TouchAndHold(x, y float64, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.TouchAndHold(x, y, options...)
return s.wdaDriver.TouchAndHold(x, y, opts...)
}
// Drag Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
// WithPressDurationOption option can be used to set pressForDuration (default to 1 second).
func (s *stubIOSDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) error {
func (s *stubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.Drag(fromX, fromY, toX, toY, options...)
return s.wdaDriver.Drag(fromX, fromY, toX, toY, opts...)
}
// SetPasteboard Sets data to the general pasteboard
@@ -289,21 +289,21 @@ func (s *stubIOSDriver) SetIme(ime string) error {
// SendKeys Types a string into active element. There must be element with keyboard focus,
// otherwise an error is raised.
// WithFrequency option can be used to set frequency of typing (letters per sec). The default value is 60
func (s *stubIOSDriver) SendKeys(text string, options ...ActionOption) error {
func (s *stubIOSDriver) SendKeys(text string, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.SendKeys(text, options...)
return s.wdaDriver.SendKeys(text, opts...)
}
// Input works like SendKeys
func (s *stubIOSDriver) Input(text string, options ...ActionOption) error {
func (s *stubIOSDriver) Input(text string, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.Input(text, options...)
return s.wdaDriver.Input(text, opts...)
}
func (s *stubIOSDriver) Clear(packageName string) error {
@@ -324,12 +324,12 @@ func (s *stubIOSDriver) PressButton(devBtn DeviceButton) error {
}
// PressBack Presses the back button
func (s *stubIOSDriver) PressBack(options ...ActionOption) error {
func (s *stubIOSDriver) PressBack(opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.PressBack(options...)
return s.wdaDriver.PressBack(opts...)
}
func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
@@ -361,12 +361,12 @@ func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) {
//return bytes.NewBuffer(imageBytes), nil
}
func (s *stubIOSDriver) TapByText(text string, options ...ActionOption) error {
func (s *stubIOSDriver) TapByText(text string, opts ...options.ActionOption) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.TapByText(text, options...)
return s.wdaDriver.TapByText(text, opts...)
}
func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error {

View File

@@ -508,9 +508,9 @@ func (wd *wdaDriver) AssertForegroundApp(bundleId string, viewControllerType ...
return nil
}
func (wd *wdaDriver) Tap(x, y float64, options ...ActionOption) (err error) {
func (wd *wdaDriver) Tap(x, y float64, opts ...options.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/tap/:uuid"] respondWithTarget:self action:@selector(handleTap:)]
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
x = wd.toScale(x)
y = wd.toScale(y)
@@ -518,31 +518,31 @@ func (wd *wdaDriver) Tap(x, y float64, options ...ActionOption) (err error) {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
x += actionOptions.GetRandomOffset()
y += actionOptions.GetRandomOffset()
data := map[string]interface{}{
"x": x,
"y": y,
}
// update data options in post data for extra WDA configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = wd.httpPOST(data, "/session", wd.session.ID, "/wda/tap/0")
return
}
func (wd *wdaDriver) DoubleTap(x, y float64, options ...ActionOption) (err error) {
func (wd *wdaDriver) DoubleTap(x, y float64, opts ...options.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/doubleTap"] respondWithTarget:self action:@selector(handleDoubleTapCoordinate:)]
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
x = wd.toScale(x)
y = wd.toScale(y)
if len(actionOptions.Offset) == 2 {
x += float64(actionOptions.Offset[0])
y += float64(actionOptions.Offset[1])
}
x += actionOptions.getRandomOffset()
y += actionOptions.getRandomOffset()
x += actionOptions.GetRandomOffset()
y += actionOptions.GetRandomOffset()
data := map[string]interface{}{
"x": x,
@@ -552,17 +552,17 @@ func (wd *wdaDriver) DoubleTap(x, y float64, options ...ActionOption) (err error
return
}
func (wd *wdaDriver) TouchAndHold(x, y float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (wd *wdaDriver) TouchAndHold(x, y float64, opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
if actionOptions.Duration == 0 {
options = append(options, WithDuration(1))
opts = append(opts, options.WithDuration(1))
}
return wd.Tap(x, y, options...)
return wd.Tap(x, y, opts...)
}
func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/dragfromtoforduration"] respondWithTarget:self action:@selector(handleDragCoordinate:)]
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
fromX = wd.toScale(fromX)
fromY = wd.toScale(fromY)
@@ -574,10 +574,10 @@ func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOptio
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
data := map[string]interface{}{
"fromX": math.Round(fromX*10) / 10,
@@ -590,15 +590,15 @@ func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, options ...ActionOptio
}
// update data options in post data for extra WDA configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
// wda 43 version
_, err = wd.httpPOST(data, "/session", wd.session.ID, "/wda/dragfromtoforduration")
// _, err = wd.httpPOST(data, "/session", wd.session.ID, "/wda/drag")
return
}
func (wd *wdaDriver) Swipe(fromX, fromY, toX, toY float64, options ...ActionOption) error {
return wd.Drag(fromX, fromY, toX, toY, options...)
func (wd *wdaDriver) Swipe(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
return wd.Drag(fromX, fromY, toX, toY, opts...)
}
func (wd *wdaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
@@ -632,34 +632,34 @@ func (wd *wdaDriver) PressKeyCode(keyCode KeyCode) (err error) {
return errDriverNotImplemented
}
func (wd *wdaDriver) SendKeys(text string, options ...ActionOption) (err error) {
func (wd *wdaDriver) SendKeys(text string, opts ...options.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/keys"] respondWithTarget:self action:@selector(handleKeys:)]
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
data := map[string]interface{}{"value": strings.Split(text, "")}
// new data options in post data for extra WDA configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = wd.httpPOST(data, "/session", wd.session.ID, "/wda/keys")
return
}
func (wd *wdaDriver) Backspace(count int, options ...ActionOption) (err error) {
func (wd *wdaDriver) Backspace(count int, opts ...options.ActionOption) (err error) {
if count == 0 {
return nil
}
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
data := map[string]interface{}{"count": count}
// new data options in post data for extra WDA configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = wd.httpPOST(data, "/gtf/interaction/input/backspace")
return
}
func (wd *wdaDriver) Input(text string, options ...ActionOption) (err error) {
return wd.SendKeys(text, options...)
func (wd *wdaDriver) Input(text string, opts ...options.ActionOption) (err error) {
return wd.SendKeys(text, opts...)
}
func (wd *wdaDriver) Clear(packageName string) error {
@@ -667,8 +667,8 @@ func (wd *wdaDriver) Clear(packageName string) error {
}
// PressBack simulates a short press on the BACK button.
func (wd *wdaDriver) PressBack(options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (wd *wdaDriver) PressBack(opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
windowSize, err := wd.WindowSize()
if err != nil {
@@ -684,10 +684,10 @@ func (wd *wdaDriver) PressBack(options ...ActionOption) (err error) {
toX += float64(actionOptions.Offset[2])
toY += float64(actionOptions.Offset[3])
}
fromX += actionOptions.getRandomOffset()
fromY += actionOptions.getRandomOffset()
toX += actionOptions.getRandomOffset()
toY += actionOptions.getRandomOffset()
fromX += actionOptions.GetRandomOffset()
fromY += actionOptions.GetRandomOffset()
toX += actionOptions.GetRandomOffset()
toY += actionOptions.GetRandomOffset()
data := map[string]interface{}{
"fromX": fromX,
@@ -697,7 +697,7 @@ func (wd *wdaDriver) PressBack(options ...ActionOption) (err error) {
}
// update data options in post data for extra WDA configurations
actionOptions.updateData(data)
actionOptions.UpdateData(data)
_, err = wd.httpPOST(data, "/session", wd.session.ID, "/wda/dragfromtoforduration")
return
@@ -826,7 +826,7 @@ func (wd *wdaDriver) Source(srcOpt ...SourceOption) (source string, err error) {
return
}
func (wd *wdaDriver) TapByText(text string, options ...ActionOption) error {
func (wd *wdaDriver) TapByText(text string, opts ...options.ActionOption) error {
return errDriverNotImplemented
}

View File

@@ -297,7 +297,8 @@ func Test_remoteWD_Drag(t *testing.T) {
setup(t)
// err := driver.Drag(200, 300, 200, 500, WithDataPressDuration(0.5))
err := driver.Drag(200, 300, 200, 500, WithPressDuration(2), WithDuration(3))
err := driver.Drag(200, 300, 200, 500,
options.WithPressDuration(2), options.WithDuration(3))
if err != nil {
t.Fatal(err)
}
@@ -352,7 +353,7 @@ func Test_remoteWD_GetPasteboard(t *testing.T) {
func Test_remoteWD_SendKeys(t *testing.T) {
setup(t)
// driver.StartCaptureLog("hrp_wda_log")
err := driver.SendKeys("test", WithIdentifier("test"))
err := driver.SendKeys("test", options.WithIdentifier("test"))
// result, _ := driver.StopCaptureLog()
// err := driver.SendKeys("App Store", WithFrequency(3))
if err != nil {

View File

@@ -8,6 +8,7 @@ import (
"syscall"
"time"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
"github.com/rs/zerolog/log"
)
@@ -28,8 +29,8 @@ type EndToEndDelay struct {
Timelines []timeLog `json:"timelines"`
}
func CollectEndToEndDelay(dExt *DriverExt, options ...ActionOption) {
dataOptions := NewActionOptions(options...)
func CollectEndToEndDelay(dExt *DriverExt, opts ...options.ActionOption) {
dataOptions := options.NewActionOptions(opts...)
startTime := time.Now()
if dataOptions.Interval == 0 {

451
pkg/uixt/options/action.go Normal file
View File

@@ -0,0 +1,451 @@
package options
import (
"math/rand/v2"
"github.com/httprunner/httprunner/v5/internal/builtin"
)
// (x1, y1) is the top left corner, (x2, y2) is the bottom right corner
// [x1, y1, x2, y2] in percentage of the screen
type Scope []float64
// [x1, y1, x2, y2] in absolute pixels
type AbsScope []int
func (s AbsScope) Option() ActionOption {
return WithAbsScope(s[0], s[1], s[2], s[3])
}
type ActionOptions struct {
// log
Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // used to identify the action in log
// control related
MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times
IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found
Interval float64 `json:"interval,omitempty" yaml:"interval,omitempty"` // interval between retries in seconds
Duration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action
PressDuration float64 `json:"press_duration,omitempty" yaml:"press_duration,omitempty"` // used to set duration of ios swipe action
Steps int `json:"steps,omitempty" yaml:"steps,omitempty"` // used to set steps of android swipe action
Direction interface{} `json:"direction,omitempty" yaml:"direction,omitempty"` // used by swipe to tap text or app
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action
Frequency int `json:"frequency,omitempty" yaml:"frequency,omitempty"`
// scope related
Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"`
AbsScope AbsScope `json:"abs_scope,omitempty" yaml:"abs_scope,omitempty"`
Regex bool `json:"regex,omitempty" yaml:"regex,omitempty"` // use regex to match text
Offset []int `json:"offset,omitempty" yaml:"offset,omitempty"` // used to tap offset of point
OffsetRandomRange []int `json:"offset_random_range,omitempty" yaml:"offset_random_range,omitempty"` // set random range [min, max] for tap/swipe points
Index int `json:"index,omitempty" yaml:"index,omitempty"` // index of the target element
MatchOne bool `json:"match_one,omitempty" yaml:"match_one,omitempty"` // match one of the targets if existed
// set custiom options such as textview, id, description
Custom map[string]interface{} `json:"custom,omitempty" yaml:"custom,omitempty"`
// screenshot related
ScreenShotWithOCR bool `json:"screenshot_with_ocr,omitempty" yaml:"screenshot_with_ocr,omitempty"`
ScreenShotWithUpload bool `json:"screenshot_with_upload,omitempty" yaml:"screenshot_with_upload,omitempty"`
ScreenShotWithLiveType bool `json:"screenshot_with_live_type,omitempty" yaml:"screenshot_with_live_type,omitempty"`
ScreenShotWithLivePopularity bool `json:"screenshot_with_live_popularity,omitempty" yaml:"screenshot_with_live_popularity,omitempty"`
ScreenShotWithUITypes []string `json:"screenshot_with_ui_types,omitempty" yaml:"screenshot_with_ui_types,omitempty"`
ScreenShotWithClosePopups bool `json:"screenshot_with_close_popups,omitempty" yaml:"screenshot_with_close_popups,omitempty"`
ScreenShotWithOCRCluster string `json:"screenshot_with_ocr_cluster,omitempty" yaml:"screenshot_with_ocr_cluster,omitempty"`
ScreenShotFileName string `json:"screenshot_file_name,omitempty" yaml:"screenshot_file_name,omitempty"`
}
func (o *ActionOptions) Options() []ActionOption {
options := make([]ActionOption, 0)
if o == nil {
return options
}
if o.Identifier != "" {
options = append(options, WithIdentifier(o.Identifier))
}
if o.MaxRetryTimes != 0 {
options = append(options, WithMaxRetryTimes(o.MaxRetryTimes))
}
if o.IgnoreNotFoundError {
options = append(options, WithIgnoreNotFoundError(true))
}
if o.Interval != 0 {
options = append(options, WithInterval(o.Interval))
}
if o.Duration != 0 {
options = append(options, WithDuration(o.Duration))
}
if o.PressDuration != 0 {
options = append(options, WithPressDuration(o.PressDuration))
}
if o.Steps != 0 {
options = append(options, WithSteps(o.Steps))
}
switch v := o.Direction.(type) {
case string:
options = append(options, WithDirection(v))
case []float64:
options = append(options, WithCustomDirection(
v[0], v[1],
v[2], v[3],
))
case []interface{}:
// loaded from json case
// custom direction: [fromX, fromY, toX, toY]
sx, _ := builtin.Interface2Float64(v[0])
sy, _ := builtin.Interface2Float64(v[1])
ex, _ := builtin.Interface2Float64(v[2])
ey, _ := builtin.Interface2Float64(v[3])
options = append(options, WithCustomDirection(
sx, sy,
ex, ey,
))
}
if o.Timeout != 0 {
options = append(options, WithTimeout(o.Timeout))
}
if o.Frequency != 0 {
options = append(options, WithFrequency(o.Frequency))
}
if len(o.AbsScope) == 4 {
options = append(options, WithAbsScope(
o.AbsScope[0], o.AbsScope[1], o.AbsScope[2], o.AbsScope[3]))
} else if len(o.Scope) == 4 {
options = append(options, WithScope(
o.Scope[0], o.Scope[1], o.Scope[2], o.Scope[3]))
}
if len(o.Offset) == 2 {
// for tap [x,y] offset
options = append(options, WithTapOffset(o.Offset[0], o.Offset[1]))
} else if len(o.Offset) == 4 {
// for swipe [fromX, fromY, toX, toY] offset
options = append(options, WithSwipeOffset(
o.Offset[0], o.Offset[1], o.Offset[2], o.Offset[3]))
}
if len(o.OffsetRandomRange) == 2 {
options = append(options, WithOffsetRandomRange(
o.OffsetRandomRange[0], o.OffsetRandomRange[1]))
}
if o.Regex {
options = append(options, WithRegex(true))
}
if o.Index != 0 {
options = append(options, WithIndex(o.Index))
}
if o.MatchOne {
options = append(options, WithMatchOne(true))
}
// custom options
if o.Custom != nil {
for k, v := range o.Custom {
options = append(options, WithCustomOption(k, v))
}
}
// screenshot options
if o.ScreenShotWithOCR {
options = append(options, WithScreenShotOCR(true))
}
if o.ScreenShotWithUpload {
options = append(options, WithScreenShotUpload(true))
}
if o.ScreenShotWithLiveType {
options = append(options, WithScreenShotLiveType(true))
}
if o.ScreenShotWithLivePopularity {
options = append(options, WithScreenShotLivePopularity(true))
}
if len(o.ScreenShotWithUITypes) > 0 {
options = append(options, WithScreenShotUITypes(o.ScreenShotWithUITypes...))
}
if o.ScreenShotWithClosePopups {
options = append(options, WithScreenShotClosePopups(true))
}
if o.ScreenShotWithOCRCluster != "" {
options = append(options, WithScreenOCRCluster(o.ScreenShotWithOCRCluster))
}
if o.ScreenShotFileName != "" {
options = append(options, WithScreenShotFileName(o.ScreenShotFileName))
}
return options
}
func (o *ActionOptions) ScreenshotActions() []string {
actions := []string{}
if o.ScreenShotWithUpload {
actions = append(actions, "upload")
}
if o.ScreenShotWithOCR {
actions = append(actions, "ocr")
}
if o.ScreenShotWithLiveType {
actions = append(actions, "liveType")
}
if o.ScreenShotWithLivePopularity {
actions = append(actions, "livePopularity")
}
// UI detection
if len(o.ScreenShotWithUITypes) > 0 {
actions = append(actions, "ui")
}
if o.ScreenShotWithClosePopups {
actions = append(actions, "close")
}
return actions
}
func (o *ActionOptions) GetRandomOffset() float64 {
if len(o.OffsetRandomRange) != 2 {
// invalid offset random range, should be [min, max]
return 0
}
minOffset := o.OffsetRandomRange[0]
maxOffset := o.OffsetRandomRange[1]
return float64(builtin.GetRandomNumber(minOffset, maxOffset)) + rand.Float64()
}
func (o *ActionOptions) UpdateData(data map[string]interface{}) {
if o.Identifier != "" {
data["log"] = map[string]interface{}{
"enable": true,
"data": o.Identifier,
}
}
if o.Steps > 0 {
data["steps"] = o.Steps
}
if _, ok := data["steps"]; !ok {
data["steps"] = 12 // default steps
}
if o.Duration > 0 {
data["duration"] = o.Duration
}
if _, ok := data["duration"]; !ok {
data["duration"] = 0 // default duration
}
if o.Frequency > 0 {
data["frequency"] = o.Frequency
}
if _, ok := data["frequency"]; !ok {
data["frequency"] = 10 // default frequency
}
if _, ok := data["replace"]; !ok {
data["replace"] = true // default true
}
// custom options
if o.Custom != nil {
for k, v := range o.Custom {
data[k] = v
}
}
}
func NewActionOptions(opts ...ActionOption) *ActionOptions {
actionOptions := &ActionOptions{}
for _, option := range opts {
option(actionOptions)
}
return actionOptions
}
type ActionOption func(o *ActionOptions)
func WithCustomOption(key string, value interface{}) ActionOption {
return func(o *ActionOptions) {
if o.Custom == nil {
o.Custom = make(map[string]interface{})
}
o.Custom[key] = value
}
}
func WithIdentifier(identifier string) ActionOption {
return func(o *ActionOptions) {
o.Identifier = identifier
}
}
func WithIndex(index int) ActionOption {
return func(o *ActionOptions) {
o.Index = index
}
}
// set alias for compatibility
var WithWaitTime = WithInterval
func WithInterval(sec float64) ActionOption {
return func(o *ActionOptions) {
o.Interval = sec
}
}
func WithDuration(duration float64) ActionOption {
return func(o *ActionOptions) {
o.Duration = duration
}
}
func WithPressDuration(pressDuration float64) ActionOption {
return func(o *ActionOptions) {
o.PressDuration = pressDuration
}
}
func WithSteps(steps int) ActionOption {
return func(o *ActionOptions) {
o.Steps = steps
}
}
// WithDirection inputs direction (up, down, left, right)
func WithDirection(direction string) ActionOption {
return func(o *ActionOptions) {
o.Direction = direction
}
}
// WithCustomDirection inputs sx, sy, ex, ey
func WithCustomDirection(sx, sy, ex, ey float64) ActionOption {
return func(o *ActionOptions) {
o.Direction = []float64{sx, sy, ex, ey}
}
}
// WithScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all in [0, 1], which means the relative position of the screen
func WithScope(x1, y1, x2, y2 float64) ActionOption {
return func(o *ActionOptions) {
o.Scope = Scope{x1, y1, x2, y2}
}
}
// WithAbsScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all absolute position of the screen
func WithAbsScope(x1, y1, x2, y2 int) ActionOption {
return func(o *ActionOptions) {
o.AbsScope = AbsScope{x1, y1, x2, y2}
}
}
// Deprecated: use WithTapOffset instead
func WithOffset(offsetX, offsetY int) ActionOption {
return func(o *ActionOptions) {
o.Offset = []int{offsetX, offsetY}
}
}
// tap [x, y] with offset [offsetX, offsetY]
var WithTapOffset = WithOffset
// swipe [fromX, fromY, toX, toY] with offset [offsetFromX, offsetFromY, offsetToX, offsetToY]
func WithSwipeOffset(offsetFromX, offsetFromY, offsetToX, offsetToY int) ActionOption {
return func(o *ActionOptions) {
o.Offset = []int{offsetFromX, offsetFromY, offsetToX, offsetToY}
}
}
func WithOffsetRandomRange(min, max int) ActionOption {
return func(o *ActionOptions) {
o.OffsetRandomRange = []int{min, max}
}
}
func WithRegex(regex bool) ActionOption {
return func(o *ActionOptions) {
o.Regex = regex
}
}
func WithMatchOne(matchOne bool) ActionOption {
return func(o *ActionOptions) {
o.MatchOne = matchOne
}
}
func WithFrequency(frequency int) ActionOption {
return func(o *ActionOptions) {
o.Frequency = frequency
}
}
func WithMaxRetryTimes(maxRetryTimes int) ActionOption {
return func(o *ActionOptions) {
o.MaxRetryTimes = maxRetryTimes
}
}
func WithTimeout(timeout int) ActionOption {
return func(o *ActionOptions) {
o.Timeout = timeout
}
}
func WithIgnoreNotFoundError(ignoreError bool) ActionOption {
return func(o *ActionOptions) {
o.IgnoreNotFoundError = ignoreError
}
}
func WithScreenShotOCR(ocrOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithOCR = ocrOn
}
}
func WithScreenShotUpload(uploadOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithUpload = uploadOn
}
}
func WithScreenShotLiveType(liveTypeOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithLiveType = liveTypeOn
}
}
func WithScreenShotLivePopularity(livePopularityOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithLivePopularity = livePopularityOn
}
}
func WithScreenShotUITypes(uiTypes ...string) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithUITypes = uiTypes
}
}
func WithScreenShotClosePopups(closeOn bool) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithClosePopups = closeOn
}
}
func WithScreenOCRCluster(ocrCluster string) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotWithOCRCluster = ocrCluster
}
}
func WithScreenShotFileName(fileName string) ActionOption {
return func(o *ActionOptions) {
o.ScreenShotFileName = fileName
}
}

View File

@@ -15,9 +15,9 @@ func (dev *HarmonyDeviceConfig) Options() (deviceOptions []HarmonyDeviceOption)
return
}
func NewHarmonyDeviceConfig(options ...HarmonyDeviceOption) (device *HarmonyDeviceConfig) {
func NewHarmonyDeviceConfig(opts ...HarmonyDeviceOption) (device *HarmonyDeviceConfig) {
device = &HarmonyDeviceConfig{}
for _, option := range options {
for _, option := range opts {
option(device)
}
return

View File

@@ -5,6 +5,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
// TODO: add more popup texts
@@ -30,7 +31,7 @@ func findTextPopup(screenTexts OCRTexts) (closePoint *OCRText) {
continue
}
points, err := screenTexts.FindTexts([]string{popup[0], popup[1]}, WithRegex(true))
points, err := screenTexts.FindTexts([]string{popup[0], popup[1]}, options.WithRegex(true))
if err == nil {
log.Warn().Interface("popup", popup).
Interface("texts", screenTexts).Msg("text popup found")
@@ -63,9 +64,9 @@ func (dExt *DriverExt) AutoPopupHandler() error {
// check popup by screenshot
screenResult, err := dExt.GetScreenResult(
WithScreenShotOCR(true),
WithScreenShotUpload(true),
WithScreenShotFileName("check_popup"),
options.WithScreenShotOCR(true),
options.WithScreenShotUpload(true),
options.WithScreenShotFileName("check_popup"),
)
if err != nil {
return errors.Wrap(err, "get screen result failed for popup handler")
@@ -98,9 +99,9 @@ func (p *PopupInfo) ClosePoint() *PointF {
func (dExt *DriverExt) CheckPopup() (popup *PopupInfo, err error) {
screenResult, err := dExt.GetScreenResult(
WithScreenShotUpload(true),
WithScreenShotClosePopups(true), // get popup area and close area
WithScreenShotFileName("check_popup"),
options.WithScreenShotUpload(true),
options.WithScreenShotClosePopups(true), // get popup area and close area
options.WithScreenShotFileName("check_popup"),
)
if err != nil {
return nil, errors.Wrap(err, "get screen result failed for popup handler")

View File

@@ -18,6 +18,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
type ScreenResult struct {
@@ -36,21 +37,21 @@ func (s *ScreenResult) FilterTextsByScope(x1, y1, x2, y2 float64) OCRTexts {
log.Warn().Msg("x1, y1, x2, y2 should be in percentage, skip filter scope")
return s.Texts
}
return s.Texts.FilterScope(AbsScope{
return s.Texts.FilterScope(options.AbsScope{
int(float64(s.Resolution.Width) * x1), int(float64(s.Resolution.Height) * y1),
int(float64(s.Resolution.Width) * x2), int(float64(s.Resolution.Height) * y2),
})
}
// GetScreenResult takes a screenshot, returns the image recognition result
func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *ScreenResult, err error) {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) GetScreenResult(opts ...options.ActionOption) (screenResult *ScreenResult, err error) {
actionOptions := options.NewActionOptions(opts...)
if actionOptions.MaxRetryTimes == 0 {
actionOptions.MaxRetryTimes = 1
}
var fileName string
screenshotActions := actionOptions.screenshotActions()
screenshotActions := actionOptions.ScreenshotActions()
if actionOptions.ScreenShotFileName != "" {
fileName = builtin.GenNameWithTimestamp("%d_" + actionOptions.ScreenShotFileName)
} else if len(screenshotActions) != 0 {
@@ -86,7 +87,7 @@ func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *S
Tags: nil,
Resolution: windowSize,
}
imageResult, err = dExt.ImageService.GetImage(bufSource, options...)
imageResult, err = dExt.ImageService.GetImage(bufSource, opts...)
if err != nil {
log.Error().Err(err).Msg("GetImage from ImageService failed")
lastErr = err
@@ -129,40 +130,40 @@ func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *S
return screenResult, nil
}
func (dExt *DriverExt) GetScreenTexts(options ...ActionOption) (ocrTexts OCRTexts, err error) {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) GetScreenTexts(opts ...options.ActionOption) (ocrTexts OCRTexts, err error) {
actionOptions := options.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
options = append(options, WithScreenShotFileName("get_screen_texts"))
opts = append(opts, options.WithScreenShotFileName("get_screen_texts"))
}
options = append(options, WithScreenShotOCR(true), WithScreenShotUpload(true))
screenResult, err := dExt.GetScreenResult(options...)
opts = append(opts, options.WithScreenShotOCR(true), options.WithScreenShotUpload(true))
screenResult, err := dExt.GetScreenResult(opts...)
if err != nil {
return
}
return screenResult.Texts, nil
}
func (dExt *DriverExt) FindUIRectInUIKit(search string, options ...ActionOption) (point PointF, err error) {
func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...options.ActionOption) (point PointF, err error) {
// find text using OCR
if !builtin.IsPathExists(search) {
return dExt.FindScreenText(search, options...)
return dExt.FindScreenText(search, opts...)
}
// TODO: find image using CV
err = errors.New("ocr text not found")
return
}
func (dExt *DriverExt) FindScreenText(text string, options ...ActionOption) (point PointF, err error) {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) FindScreenText(text string, opts ...options.ActionOption) (point PointF, err error) {
actionOptions := options.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
options = append(options, WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text)))
opts = append(opts, options.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text)))
}
ocrTexts, err := dExt.GetScreenTexts(options...)
ocrTexts, err := dExt.GetScreenTexts(opts...)
if err != nil {
return
}
result, err := ocrTexts.FindText(text, dExt.ParseActionOptions(options...)...)
result, err := ocrTexts.FindText(text, dExt.ParseActionOptions(opts...)...)
if err != nil {
log.Warn().Msgf("FindText failed: %s", err.Error())
return
@@ -174,14 +175,14 @@ func (dExt *DriverExt) FindScreenText(text string, options ...ActionOption) (poi
return
}
func (dExt *DriverExt) FindUIResult(options ...ActionOption) (point PointF, err error) {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) FindUIResult(opts ...options.ActionOption) (point PointF, err error) {
actionOptions := options.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
options = append(options, WithScreenShotFileName(
opts = append(opts, options.WithScreenShotFileName(
fmt.Sprintf("find_ui_result_%s", strings.Join(actionOptions.ScreenShotWithUITypes, "_"))))
}
screenResult, err := dExt.GetScreenResult(options...)
screenResult, err := dExt.GetScreenResult(opts...)
if err != nil {
return
}
@@ -190,7 +191,7 @@ func (dExt *DriverExt) FindUIResult(options ...ActionOption) (point PointF, err
if err != nil {
return
}
uiResult, err := uiResults.GetUIResult(dExt.ParseActionOptions(options...)...)
uiResult, err := uiResults.GetUIResult(dExt.ParseActionOptions(opts...)...)
point = uiResult.Center()
log.Info().Interface("text", actionOptions.ScreenShotWithUITypes).

View File

@@ -4,10 +4,11 @@ import (
"fmt"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
"github.com/pkg/errors"
)
func (dExt *DriverExt) Drag(fromX, fromY, toX, toY float64, options ...ActionOption) (err error) {
func (dExt *DriverExt) Drag(fromX, fromY, toX, toY float64, opts ...options.ActionOption) (err error) {
windowSize, err := dExt.Driver.WindowSize()
if err != nil {
return errors.Wrap(code.DeviceGetInfoError, err.Error())
@@ -25,5 +26,5 @@ func (dExt *DriverExt) Drag(fromX, fromY, toX, toY float64, options ...ActionOpt
toX = float64(width) * toX
toY = float64(height) * toY
return dExt.Driver.Drag(fromX, fromY, toX, toY, options...)
return dExt.Driver.Drag(fromX, fromY, toX, toY, opts...)
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
func assertRelative(p float64) bool {
@@ -17,7 +18,7 @@ func assertRelative(p float64) bool {
}
// SwipeRelative swipe from relative position [fromX, fromY] to relative position [toX, toY]
func (dExt *DriverExt) SwipeRelative(fromX, fromY, toX, toY float64, options ...ActionOption) error {
func (dExt *DriverExt) SwipeRelative(fromX, fromY, toX, toY float64, opts ...options.ActionOption) error {
if !assertRelative(fromX) || !assertRelative(fromY) ||
!assertRelative(toX) || !assertRelative(toY) {
return errors.Wrap(code.InvalidCaseError,
@@ -36,33 +37,33 @@ func (dExt *DriverExt) SwipeRelative(fromX, fromY, toX, toY float64, options ...
fromY = float64(height) * fromY
toX = float64(width) * toX
toY = float64(height) * toY
err = dExt.Driver.Swipe(fromX, fromY, toX, toY, options...)
err = dExt.Driver.Swipe(fromX, fromY, toX, toY, opts...)
if err != nil {
return errors.Wrap(code.MobileUISwipeError, err.Error())
}
return nil
}
func (dExt *DriverExt) SwipeUp(options ...ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.5, 0.1, options...)
func (dExt *DriverExt) SwipeUp(opts ...options.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.5, 0.1, opts...)
}
func (dExt *DriverExt) SwipeDown(options ...ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.5, 0.9, options...)
func (dExt *DriverExt) SwipeDown(opts ...options.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.5, 0.9, opts...)
}
func (dExt *DriverExt) SwipeLeft(options ...ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.1, 0.5, options...)
func (dExt *DriverExt) SwipeLeft(opts ...options.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.1, 0.5, opts...)
}
func (dExt *DriverExt) SwipeRight(options ...ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.9, 0.5, options...)
func (dExt *DriverExt) SwipeRight(opts ...options.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.9, 0.5, opts...)
}
type Action func(driver *DriverExt) error
func (dExt *DriverExt) LoopUntil(findAction, findCondition, foundAction Action, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) LoopUntil(findAction, findCondition, foundAction Action, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
maxRetryTimes := actionOptions.MaxRetryTimes
interval := actionOptions.Interval
@@ -84,8 +85,8 @@ func (dExt *DriverExt) LoopUntil(findAction, findCondition, foundAction Action,
fmt.Sprintf("loop %d times, match find condition failed", maxRetryTimes))
}
func (dExt *DriverExt) prepareSwipeAction(params interface{}, options ...ActionOption) func(d *DriverExt) error {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) prepareSwipeAction(params interface{}, opts ...options.ActionOption) func(d *DriverExt) error {
actionOptions := options.NewActionOptions(opts...)
var swipeDirection interface{}
// priority: params > actionOptions.Direction, default swipe up
@@ -111,20 +112,20 @@ func (dExt *DriverExt) prepareSwipeAction(params interface{}, options ...ActionO
// enum direction: up, down, left, right
switch d {
case "up":
return dExt.SwipeUp(options...)
return dExt.SwipeUp(opts...)
case "down":
return dExt.SwipeDown(options...)
return dExt.SwipeDown(opts...)
case "left":
return dExt.SwipeLeft(options...)
return dExt.SwipeLeft(opts...)
case "right":
return dExt.SwipeRight(options...)
return dExt.SwipeRight(opts...)
default:
return errors.Wrap(code.InvalidParamError,
fmt.Sprintf("get unexpected swipe direction: %s", d))
}
} else if params, err := builtin.ConvertToFloat64Slice(swipeDirection); err == nil && len(params) == 4 {
// custom direction: [fromX, fromY, toX, toY]
if err := dExt.SwipeRelative(params[0], params[1], params[2], params[3], options...); err != nil {
if err := dExt.SwipeRelative(params[0], params[1], params[2], params[3], opts...); err != nil {
log.Error().Err(err).Msgf("swipe from (%v, %v) to (%v, %v) failed",
params[0], params[1], params[2], params[3])
return err
@@ -136,22 +137,22 @@ func (dExt *DriverExt) prepareSwipeAction(params interface{}, options ...ActionO
}
}
func (dExt *DriverExt) swipeToTapTexts(texts []string, options ...ActionOption) error {
func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...options.ActionOption) error {
if len(texts) == 0 {
return errors.New("no text to tap")
}
options = append(options, WithMatchOne(true), WithRegex(true))
actionOptions := NewActionOptions(options...)
opts = append(opts, options.WithMatchOne(true), options.WithRegex(true))
actionOptions := options.NewActionOptions(opts...)
actionOptions.Identifier = ""
optionsWithoutIdentifier := actionOptions.Options()
var point PointF
findTexts := func(d *DriverExt) error {
var err error
screenResult, err := d.GetScreenResult(
WithScreenShotOCR(true),
WithScreenShotUpload(true),
WithScreenShotFileName(
options.WithScreenShotOCR(true),
options.WithScreenShotUpload(true),
options.WithScreenShotFileName(
fmt.Sprintf("swipe_to_tap_texts_%s", strings.Join(texts, "_")),
),
)
@@ -172,14 +173,14 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, options ...ActionOption)
}
foundTextAction := func(d *DriverExt) error {
// tap text
return d.TapAbsXY(point.X, point.Y, options...)
return d.TapAbsXY(point.X, point.Y, opts...)
}
findAction := dExt.prepareSwipeAction(nil, optionsWithoutIdentifier...)
return dExt.LoopUntil(findAction, findTexts, foundTextAction, optionsWithoutIdentifier...)
}
func (dExt *DriverExt) swipeToTapApp(appName string, options ...ActionOption) error {
func (dExt *DriverExt) swipeToTapApp(appName string, opts ...options.ActionOption) error {
// go to home screen
if err := dExt.Driver.Homescreen(); err != nil {
return errors.Wrap(err, "go to home screen failed")
@@ -195,21 +196,21 @@ func (dExt *DriverExt) swipeToTapApp(appName string, options ...ActionOption) er
dExt.SwipeRight()
}
options = append(options, WithDirection("left"))
opts = append(opts, options.WithDirection("left"))
actionOptions := NewActionOptions(options...)
actionOptions := options.NewActionOptions(opts...)
// default to retry 5 times
if actionOptions.MaxRetryTimes == 0 {
options = append(options, WithMaxRetryTimes(5))
opts = append(opts, options.WithMaxRetryTimes(5))
}
// tap app icon above the text
if len(actionOptions.Offset) == 0 {
options = append(options, WithTapOffset(0, -25))
opts = append(opts, options.WithTapOffset(0, -25))
}
// set default swipe interval to 1 second
if builtin.IsZeroFloat64(actionOptions.Interval) {
options = append(options, WithInterval(1))
opts = append(opts, options.WithInterval(1))
}
return dExt.swipeToTapTexts([]string{appName}, options...)
return dExt.swipeToTapTexts([]string{appName}, opts...)
}

View File

@@ -4,16 +4,18 @@ package uixt
import (
"testing"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
func TestAndroidSwipeAction(t *testing.T) {
setupAndroidAdbDriver(t)
swipeAction := driverExt.prepareSwipeAction("up", WithDirection("down"))
swipeAction := driverExt.prepareSwipeAction("up", options.WithDirection("down"))
err := swipeAction(driverExt)
checkErr(t, err)
swipeAction = driverExt.prepareSwipeAction("up", WithCustomDirection(0.5, 0.5, 0.5, 0.9))
swipeAction = driverExt.prepareSwipeAction("up", options.WithCustomDirection(0.5, 0.5, 0.5, 0.9))
err = swipeAction(driverExt)
checkErr(t, err)
}
@@ -31,6 +33,6 @@ func TestAndroidSwipeToTapTexts(t *testing.T) {
err := driverExt.Driver.AppLaunch("com.ss.android.ugc.aweme")
checkErr(t, err)
err = driverExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, WithDirection("up"))
err = driverExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, options.WithDirection("up"))
checkErr(t, err)
}

View File

@@ -6,18 +6,19 @@ import (
"github.com/pkg/errors"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
func (dExt *DriverExt) TapAbsXY(x, y float64, options ...ActionOption) error {
func (dExt *DriverExt) TapAbsXY(x, y float64, opts ...options.ActionOption) error {
// tap on absolute coordinate [x, y]
err := dExt.Driver.Tap(x, y, options...)
err := dExt.Driver.Tap(x, y, opts...)
if err != nil {
return errors.Wrap(code.MobileUITapError, err.Error())
}
return nil
}
func (dExt *DriverExt) TapXY(x, y float64, options ...ActionOption) error {
func (dExt *DriverExt) TapXY(x, y float64, opts ...options.ActionOption) error {
// tap on [x, y] percent of window size
if x > 1 || y > 1 {
return fmt.Errorf("x, y percentage should be <= 1, got x=%v, y=%v", x, y)
@@ -29,16 +30,16 @@ func (dExt *DriverExt) TapXY(x, y float64, options ...ActionOption) error {
}
x = x * float64(windowSize.Width)
y = y * float64(windowSize.Height)
return dExt.TapAbsXY(x, y, options...)
return dExt.TapAbsXY(x, y, opts...)
}
func (dExt *DriverExt) TapByOCR(ocrText string, options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) TapByOCR(ocrText string, opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
options = append(options, WithScreenShotFileName(fmt.Sprintf("tap_by_ocr_%s", ocrText)))
opts = append(opts, options.WithScreenShotFileName(fmt.Sprintf("tap_by_ocr_%s", ocrText)))
}
point, err := dExt.FindScreenText(ocrText, options...)
point, err := dExt.FindScreenText(ocrText, opts...)
if err != nil {
if actionOptions.IgnoreNotFoundError {
return nil
@@ -46,13 +47,13 @@ func (dExt *DriverExt) TapByOCR(ocrText string, options ...ActionOption) error {
return err
}
return dExt.TapAbsXY(point.X, point.Y, options...)
return dExt.TapAbsXY(point.X, point.Y, opts...)
}
func (dExt *DriverExt) TapByUIDetection(options ...ActionOption) error {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) TapByUIDetection(opts ...options.ActionOption) error {
actionOptions := options.NewActionOptions(opts...)
point, err := dExt.FindUIResult(options...)
point, err := dExt.FindUIResult(opts...)
if err != nil {
if actionOptions.IgnoreNotFoundError {
return nil
@@ -60,17 +61,17 @@ func (dExt *DriverExt) TapByUIDetection(options ...ActionOption) error {
return err
}
return dExt.TapAbsXY(point.X, point.Y, options...)
return dExt.TapAbsXY(point.X, point.Y, opts...)
}
func (dExt *DriverExt) Tap(param string, options ...ActionOption) error {
return dExt.TapOffset(param, 0, 0, options...)
func (dExt *DriverExt) Tap(param string, opts ...options.ActionOption) error {
return dExt.TapOffset(param, 0, 0, opts...)
}
func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, options ...ActionOption) (err error) {
actionOptions := NewActionOptions(options...)
func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, opts ...options.ActionOption) (err error) {
actionOptions := options.NewActionOptions(opts...)
point, err := dExt.FindUIRectInUIKit(param, options...)
point, err := dExt.FindUIRectInUIKit(param, opts...)
if err != nil {
if actionOptions.IgnoreNotFoundError {
return nil
@@ -78,10 +79,10 @@ func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, options
return err
}
return dExt.TapAbsXY(point.X+xOffset, point.Y+yOffset, options...)
return dExt.TapAbsXY(point.X+xOffset, point.Y+yOffset, opts...)
}
func (dExt *DriverExt) DoubleTapXY(x, y float64, options ...ActionOption) error {
func (dExt *DriverExt) DoubleTapXY(x, y float64, opts ...options.ActionOption) error {
// double tap on coordinate: [x, y] should be relative
if x > 1 || y > 1 {
return fmt.Errorf("x, y percentage should be < 1, got x=%v, y=%v", x, y)
@@ -93,24 +94,24 @@ func (dExt *DriverExt) DoubleTapXY(x, y float64, options ...ActionOption) error
}
x = x * float64(windowSize.Width)
y = y * float64(windowSize.Height)
err = dExt.Driver.DoubleTap(x, y, options...)
err = dExt.Driver.DoubleTap(x, y, opts...)
if err != nil {
return errors.Wrap(code.MobileUITapError, err.Error())
}
return nil
}
func (dExt *DriverExt) DoubleTap(param string, options ...ActionOption) (err error) {
return dExt.DoubleTapOffset(param, 0, 0, options...)
func (dExt *DriverExt) DoubleTap(param string, opts ...options.ActionOption) (err error) {
return dExt.DoubleTapOffset(param, 0, 0, opts...)
}
func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64, options ...ActionOption) (err error) {
func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64, opts ...options.ActionOption) (err error) {
point, err := dExt.FindUIRectInUIKit(param)
if err != nil {
return err
}
err = dExt.Driver.DoubleTap(point.X+xOffset, point.Y+yOffset, options...)
err = dExt.Driver.DoubleTap(point.X+xOffset, point.Y+yOffset, opts...)
if err != nil {
return errors.Wrap(code.MobileUITapError, err.Error())
}

View File

@@ -1,6 +1,8 @@
package server
import "github.com/httprunner/httprunner/v5/pkg/uixt"
import (
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
type HttpResponse struct {
Code int `json:"code"`
@@ -13,7 +15,7 @@ type TapRequest struct {
Y float64 `json:"y"`
Text string `json:"text"`
Options *uixt.ActionOptions `json:"options,omitempty"`
Options *options.ActionOptions `json:"options,omitempty"`
}
type DragRequest struct {
@@ -22,7 +24,7 @@ type DragRequest struct {
ToX float64 `json:"to_x"`
ToY float64 `json:"to_y"`
Options *uixt.ActionOptions `json:"options,omitempty"`
Options *options.ActionOptions `json:"options,omitempty"`
}
type InputRequest struct {
@@ -31,7 +33,7 @@ type InputRequest struct {
}
type ScreenRequest struct {
Options *uixt.ActionOptions `json:"options,omitempty"`
Options *options.ActionOptions `json:"options,omitempty"`
}
type KeycodeRequest struct {

View File

@@ -8,6 +8,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
"github.com/rs/zerolog/log"
)
@@ -51,7 +52,7 @@ func screenResultHandler(c *gin.Context) {
return
}
var actionOptions []uixt.ActionOption
var actionOptions []options.ActionOption
if screenReq.Options != nil {
actionOptions = screenReq.Options.Options()
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
"github.com/rs/zerolog/log"
)
@@ -22,7 +22,7 @@ func tapHandler(c *gin.Context) {
return
}
var actionOptions []uixt.ActionOption
var actionOptions []options.ActionOption
if tapReq.Options != nil {
actionOptions = tapReq.Options.Options()
}
@@ -82,7 +82,7 @@ func dragHandler(c *gin.Context) {
return
}
var actionOptions []uixt.ActionOption
var actionOptions []options.ActionOption
if dragReq.Options != nil {
actionOptions = dragReq.Options.Options()
}
@@ -139,7 +139,8 @@ func inputHandler(c *gin.Context) {
return
}
err = dExt.Driver.SendKeys(inputReq.Text, uixt.WithFrequency(inputReq.Frequency))
err = dExt.Driver.SendKeys(inputReq.Text,
options.WithFrequency(inputReq.Frequency))
if err != nil {
log.Err(err).Msg(fmt.Sprintf("[%s]: failed to input text %s", c.HandlerName(), inputReq.Text))
c.JSON(http.StatusInternalServerError,

View File

@@ -11,6 +11,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/options"
)
type MobileUI struct {
@@ -103,11 +104,11 @@ func (s *StepMobile) Home() *StepMobile {
}
// TapXY taps the point {X,Y}, X & Y is percentage of coordinates
func (s *StepMobile) TapXY(x, y float64, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) TapXY(x, y float64, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_TapXY,
Params: []float64{x, y},
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -115,11 +116,11 @@ func (s *StepMobile) TapXY(x, y float64, options ...uixt.ActionOption) *StepMobi
}
// TapAbsXY taps the point {X,Y}, X & Y is absolute coordinates
func (s *StepMobile) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) TapAbsXY(x, y float64, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_TapAbsXY,
Params: []float64{x, y},
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -127,11 +128,11 @@ func (s *StepMobile) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepM
}
// Tap taps on the target element
func (s *StepMobile) Tap(params string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) Tap(params string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Tap,
Params: params,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -139,11 +140,11 @@ func (s *StepMobile) Tap(params string, options ...uixt.ActionOption) *StepMobil
}
// TapByOCR taps on the target element by OCR recognition
func (s *StepMobile) TapByOCR(ocrText string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) TapByOCR(ocrText string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_TapByOCR,
Params: ocrText,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -151,11 +152,11 @@ func (s *StepMobile) TapByOCR(ocrText string, options ...uixt.ActionOption) *Ste
}
// TapByCV taps on the target element by CV recognition
func (s *StepMobile) TapByCV(imagePath string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) TapByCV(imagePath string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_TapByCV,
Params: imagePath,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -163,10 +164,10 @@ func (s *StepMobile) TapByCV(imagePath string, options ...uixt.ActionOption) *St
}
// TapByUITypes taps on the target element specified by uiTypes, the higher the uiTypes, the higher the priority
func (s *StepMobile) TapByUITypes(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) TapByUITypes(opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_TapByCV,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -174,31 +175,31 @@ func (s *StepMobile) TapByUITypes(options ...uixt.ActionOption) *StepMobile {
}
// DoubleTapXY double taps the point {X,Y}, X & Y is percentage of coordinates
func (s *StepMobile) DoubleTapXY(x, y float64, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) DoubleTapXY(x, y float64, opts ...options.ActionOption) *StepMobile {
s.obj().Actions = append(s.obj().Actions, uixt.MobileAction{
Method: uixt.ACTION_DoubleTapXY,
Params: []float64{x, y},
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
})
return s
}
func (s *StepMobile) DoubleTap(params string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) DoubleTap(params string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_DoubleTap,
Params: params,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) Back(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) Back(opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Back,
Params: nil,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -206,99 +207,99 @@ func (s *StepMobile) Back(options ...uixt.ActionOption) *StepMobile {
}
// Swipe drags from [sx, sy] to [ex, ey]
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) Swipe(sx, sy, ex, ey float64, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: []float64{sx, sy, ex, ey},
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeUp(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeUp(opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "up",
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeDown(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeDown(opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "down",
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeLeft(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeLeft(opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "left",
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeRight(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeRight(opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "right",
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeToTapApp(appName string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeToTapApp(appName string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapApp,
Params: appName,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeToTapText(text string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeToTapText(text string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapText,
Params: text,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) SwipeToTapTexts(texts interface{}, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) SwipeToTapTexts(texts interface{}, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapTexts,
Params: texts,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) Input(text string, options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) Input(text string, opts ...options.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Input,
Params: text,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
}
s.obj().Actions = append(s.obj().Actions, action)
@@ -351,20 +352,20 @@ func (s *StepMobile) SleepRandom(params ...float64) *StepMobile {
return s
}
func (s *StepMobile) EndToEndDelay(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) EndToEndDelay(opts ...options.ActionOption) *StepMobile {
s.obj().Actions = append(s.obj().Actions, uixt.MobileAction{
Method: uixt.ACTION_EndToEndDelay,
Params: nil,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
})
return s
}
func (s *StepMobile) ScreenShot(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) ScreenShot(opts ...options.ActionOption) *StepMobile {
s.obj().Actions = append(s.obj().Actions, uixt.MobileAction{
Method: uixt.ACTION_ScreenShot,
Params: nil,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
})
return s
}
@@ -392,11 +393,11 @@ func (s *StepMobile) DisableAutoPopupHandler() *StepMobile {
return s
}
func (s *StepMobile) ClosePopups(options ...uixt.ActionOption) *StepMobile {
func (s *StepMobile) ClosePopups(opts ...options.ActionOption) *StepMobile {
s.obj().Actions = append(s.obj().Actions, uixt.MobileAction{
Method: uixt.ACTION_ClosePopups,
Params: nil,
Options: uixt.NewActionOptions(options...),
Options: options.NewActionOptions(opts...),
})
return s
}