mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-07 08:02:42 +08:00
refactor: move action options to pkg/uixt/options/action
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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")),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
// 退出回到推荐页
|
||||
|
||||
@@ -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)
|
||||
// 退出回到推荐页
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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-5s,40% 随机间隔 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").
|
||||
|
||||
@@ -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,截图保存
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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("进入直播间")),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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("输入搜索关键词")),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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")),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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()...)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
451
pkg/uixt/options/action.go
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user