diff --git a/compat.go b/compat.go index 3fd52bb8..7a917794 100644 --- a/compat.go +++ b/compat.go @@ -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) diff --git a/examples/uitest/android_e2e_delay_test.go b/examples/uitest/android_e2e_delay_test.go index cbd38009..f6bb46a3 100644 --- a/examples/uitest/android_e2e_delay_test.go +++ b/examples/uitest/android_e2e_delay_test.go @@ -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")), }, } diff --git a/examples/uitest/bili/android/cli.go b/examples/uitest/bili/android/cli.go index ec6d6029..6e119a4b 100644 --- a/examples/uitest/bili/android/cli.go +++ b/examples/uitest/bili/android/cli.go @@ -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) // 退出回到推荐页 diff --git a/examples/uitest/bili/ios/cli.go b/examples/uitest/bili/ios/cli.go index 1f8db695..8b9d2451 100644 --- a/examples/uitest/bili/ios/cli.go +++ b/examples/uitest/bili/ios/cli.go @@ -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) // 退出回到推荐页 diff --git a/examples/uitest/demo_android_feed_swipe_test.go b/examples/uitest/demo_android_feed_swipe_test.go index 75fbd11e..e6c826cb 100644 --- a/examples/uitest/demo_android_feed_swipe_test.go +++ b/examples/uitest/demo_android_feed_swipe_test.go @@ -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(). diff --git a/examples/uitest/demo_android_live_swipe_test.go b/examples/uitest/demo_android_live_swipe_test.go index 55c0778b..a03f65a8 100644 --- a/examples/uitest/demo_android_live_swipe_test.go +++ b/examples/uitest/demo_android_live_swipe_test.go @@ -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"). diff --git a/examples/uitest/demo_douyin_follow_live_test.go b/examples/uitest/demo_douyin_follow_live_test.go index 0bcf97c9..76b2e24c 100644 --- a/examples/uitest/demo_douyin_follow_live_test.go +++ b/examples/uitest/demo_douyin_follow_live_test.go @@ -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,截图保存 }, } diff --git a/examples/uitest/demo_ios_live_swipe_test.go b/examples/uitest/demo_ios_live_swipe_test.go index 817b0004..a68c8d06 100644 --- a/examples/uitest/demo_ios_live_swipe_test.go +++ b/examples/uitest/demo_ios_live_swipe_test.go @@ -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("进入直播间")), }, } diff --git a/examples/uitest/demo_ios_wda_log_test.go b/examples/uitest/demo_ios_wda_log_test.go index 041e1f99..3bb28f98 100644 --- a/examples/uitest/demo_ios_wda_log_test.go +++ b/examples/uitest/demo_ios_wda_log_test.go @@ -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("输入搜索关键词")), }, } diff --git a/examples/uitest/expert_test.go b/examples/uitest/expert_test.go index f8e3ae5a..d728b328 100644 --- a/examples/uitest/expert_test.go +++ b/examples/uitest/expert_test.go @@ -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(). diff --git a/examples/uitest/harmony_e2e_delay_test.go b/examples/uitest/harmony_e2e_delay_test.go index a74dd87b..2b4421f4 100644 --- a/examples/uitest/harmony_e2e_delay_test.go +++ b/examples/uitest/harmony_e2e_delay_test.go @@ -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")), }, } diff --git a/examples/worldcup/main_test.go b/examples/worldcup/main_test.go index 52044d20..f1b136fd 100644 --- a/examples/worldcup/main_test.go +++ b/examples/worldcup/main_test.go @@ -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(). diff --git a/pkg/uixt/action.go b/pkg/uixt/action.go index 4e6317ce..c3db6d53 100644 --- a/pkg/uixt/action.go +++ b/pkg/uixt/action.go @@ -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()...) } diff --git a/pkg/uixt/ai.go b/pkg/uixt/ai.go index 565cc4d8..52ccb6e0 100644 --- a/pkg/uixt/ai.go +++ b/pkg/uixt/ai.go @@ -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 { diff --git a/pkg/uixt/ai_vedem.go b/pkg/uixt/ai_vedem.go index 324bd8b7..b0bcf578 100644 --- a/pkg/uixt/ai_vedem.go +++ b/pkg/uixt/ai_vedem.go @@ -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 diff --git a/pkg/uixt/ai_vedem_test.go b/pkg/uixt/ai_vedem_test.go index fb100a56..7f3f5a19 100644 --- a/pkg/uixt/ai_vedem_test.go +++ b/pkg/uixt/ai_vedem_test.go @@ -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) } diff --git a/pkg/uixt/android_device.go b/pkg/uixt/android_device.go index 73434a75..3ebcfea9 100644 --- a/pkg/uixt/android_device.go +++ b/pkg/uixt/android_device.go @@ -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) { diff --git a/pkg/uixt/android_driver_adb.go b/pkg/uixt/android_driver_adb.go index f0c97135..61f55ed2 100644 --- a/pkg/uixt/android_driver_adb.go +++ b/pkg/uixt/android_driver_adb.go @@ -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 _, 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 } } diff --git a/pkg/uixt/android_driver_stub_test.go b/pkg/uixt/android_driver_stub_test.go index 57c0cc6f..258e2faa 100644 --- a/pkg/uixt/android_driver_stub_test.go +++ b/pkg/uixt/android_driver_stub_test.go @@ -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) } diff --git a/pkg/uixt/android_driver_uia2.go b/pkg/uixt/android_driver_uia2.go index d9f005f0..77aec737 100644 --- a/pkg/uixt/android_driver_uia2.go +++ b/pkg/uixt/android_driver_uia2.go @@ -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 { diff --git a/pkg/uixt/android_test.go b/pkg/uixt/android_test.go index 92d0bcc2..e689bbac 100644 --- a/pkg/uixt/android_test.go +++ b/pkg/uixt/android_test.go @@ -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 { diff --git a/pkg/uixt/ext.go b/pkg/uixt/ext.go index 890952de..494fab95 100644 --- a/pkg/uixt/ext.go +++ b/pkg/uixt/ext.go @@ -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") } diff --git a/pkg/uixt/harmony_device.go b/pkg/uixt/harmony_device.go index 86a2d593..bc74021e 100644 --- a/pkg/uixt/harmony_device.go +++ b/pkg/uixt/harmony_device.go @@ -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 } diff --git a/pkg/uixt/harmony_driver_hdc.go b/pkg/uixt/harmony_driver_hdc.go index d6e865f6..dccf31c7 100644 --- a/pkg/uixt/harmony_driver_hdc.go +++ b/pkg/uixt/harmony_driver_hdc.go @@ -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 } diff --git a/pkg/uixt/install.go b/pkg/uixt/install.go index 1a7cdb81..479d56a2 100644 --- a/pkg/uixt/install.go +++ b/pkg/uixt/install.go @@ -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") diff --git a/pkg/uixt/interface.go b/pkg/uixt/interface.go index 2cf5d641..aeddaf31 100644 --- a/pkg/uixt/interface.go +++ b/pkg/uixt/interface.go @@ -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 diff --git a/pkg/uixt/ios_device.go b/pkg/uixt/ios_device.go index 656016c8..212fd995 100644 --- a/pkg/uixt/ios_device.go +++ b/pkg/uixt/ios_device.go @@ -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 } diff --git a/pkg/uixt/ios_driver_stub.go b/pkg/uixt/ios_driver_stub.go index 044485bc..f10607a5 100644 --- a/pkg/uixt/ios_driver_stub.go +++ b/pkg/uixt/ios_driver_stub.go @@ -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 { diff --git a/pkg/uixt/ios_driver_wda.go b/pkg/uixt/ios_driver_wda.go index a9620fad..37d511d1 100644 --- a/pkg/uixt/ios_driver_wda.go +++ b/pkg/uixt/ios_driver_wda.go @@ -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 } diff --git a/pkg/uixt/ios_test.go b/pkg/uixt/ios_test.go index ad43bd03..53c85b37 100644 --- a/pkg/uixt/ios_test.go +++ b/pkg/uixt/ios_test.go @@ -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 { diff --git a/pkg/uixt/live_e2e.go b/pkg/uixt/live_e2e.go index f0118913..19a7a21d 100644 --- a/pkg/uixt/live_e2e.go +++ b/pkg/uixt/live_e2e.go @@ -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 { diff --git a/pkg/uixt/options/action.go b/pkg/uixt/options/action.go new file mode 100644 index 00000000..3b971f4e --- /dev/null +++ b/pkg/uixt/options/action.go @@ -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 + } +} diff --git a/pkg/uixt/options/harmony.go b/pkg/uixt/options/harmony.go index 83cd04da..43804a34 100644 --- a/pkg/uixt/options/harmony.go +++ b/pkg/uixt/options/harmony.go @@ -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 diff --git a/pkg/uixt/popups.go b/pkg/uixt/popups.go index 83200650..f25815b9 100644 --- a/pkg/uixt/popups.go +++ b/pkg/uixt/popups.go @@ -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") diff --git a/pkg/uixt/screenshot.go b/pkg/uixt/screenshot.go index 90aebcbc..14cfd799 100644 --- a/pkg/uixt/screenshot.go +++ b/pkg/uixt/screenshot.go @@ -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). diff --git a/pkg/uixt/ui_driver_drag.go b/pkg/uixt/ui_driver_drag.go index e410c39d..420db696 100644 --- a/pkg/uixt/ui_driver_drag.go +++ b/pkg/uixt/ui_driver_drag.go @@ -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...) } diff --git a/pkg/uixt/ui_swipe.go b/pkg/uixt/ui_swipe.go index ee7b912e..26d2fd60 100644 --- a/pkg/uixt/ui_swipe.go +++ b/pkg/uixt/ui_swipe.go @@ -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...) } diff --git a/pkg/uixt/ui_swipe_test.go b/pkg/uixt/ui_swipe_test.go index d2dd5468..7b62870c 100644 --- a/pkg/uixt/ui_swipe_test.go +++ b/pkg/uixt/ui_swipe_test.go @@ -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) } diff --git a/pkg/uixt/ui_tap.go b/pkg/uixt/ui_tap.go index e10b129c..0e2c3b07 100644 --- a/pkg/uixt/ui_tap.go +++ b/pkg/uixt/ui_tap.go @@ -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()) } diff --git a/server/model.go b/server/model.go index 0fd8458b..f5c2ee57 100644 --- a/server/model.go +++ b/server/model.go @@ -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 { diff --git a/server/source.go b/server/source.go index 8b95a96d..0df90be6 100644 --- a/server/source.go +++ b/server/source.go @@ -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() } diff --git a/server/ui.go b/server/ui.go index ec90b6d5..9df5a2b0 100644 --- a/server/ui.go +++ b/server/ui.go @@ -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, diff --git a/step_mobile_ui.go b/step_mobile_ui.go index 386f901c..b8ab029a 100644 --- a/step_mobile_ui.go +++ b/step_mobile_ui.go @@ -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 }