refactor: move action options

This commit is contained in:
lilong.129
2025-05-25 00:15:18 +08:00
parent 97dad38b7b
commit 4ff2692f02
15 changed files with 230 additions and 230 deletions
+49 -118
View File
@@ -14,78 +14,8 @@ import (
"github.com/httprunner/httprunner/v5/uixt/option"
)
type ActionMethod string
const (
ACTION_LOG ActionMethod = "log"
ACTION_AppInstall ActionMethod = "install"
ACTION_AppUninstall ActionMethod = "uninstall"
ACTION_WebLoginNoneUI ActionMethod = "login_none_ui"
ACTION_AppClear ActionMethod = "app_clear"
ACTION_AppStart ActionMethod = "app_start"
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
ACTION_AppTerminate ActionMethod = "app_terminate"
ACTION_AppStop ActionMethod = "app_stop"
ACTION_ScreenShot ActionMethod = "screenshot"
ACTION_Sleep ActionMethod = "sleep"
ACTION_SleepMS ActionMethod = "sleep_ms"
ACTION_SleepRandom ActionMethod = "sleep_random"
ACTION_SetIme ActionMethod = "set_ime"
ACTION_GetSource ActionMethod = "get_source"
ACTION_GetForegroundApp ActionMethod = "get_foreground_app"
ACTION_CallFunction ActionMethod = "call_function"
// UI handling
ACTION_Home ActionMethod = "home"
ACTION_TapXY ActionMethod = "tap_xy"
ACTION_TapAbsXY ActionMethod = "tap_abs_xy"
ACTION_TapByOCR ActionMethod = "tap_ocr"
ACTION_TapByCV ActionMethod = "tap_cv"
ACTION_DoubleTapXY ActionMethod = "double_tap_xy"
ACTION_Swipe ActionMethod = "swipe"
ACTION_Drag ActionMethod = "drag"
ACTION_Input ActionMethod = "input"
ACTION_Back ActionMethod = "back"
ACTION_KeyCode ActionMethod = "keycode"
ACTION_AIAction ActionMethod = "ai_action" // action with ai
ACTION_TapBySelector ActionMethod = "tap_by_selector"
ACTION_HoverBySelector ActionMethod = "hover_by_selector"
ACTION_WebCloseTab ActionMethod = "web_close_tab"
ACTION_SecondaryClick ActionMethod = "secondary_click"
ACTION_SecondaryClickBySelector ActionMethod = "secondary_click_by_selector"
ACTION_GetElementTextBySelector ActionMethod = "get_element_text_by_selector"
// custom actions
ACTION_SwipeToTapApp ActionMethod = "swipe_to_tap_app" // swipe left & right to find app and tap
ACTION_SwipeToTapText ActionMethod = "swipe_to_tap_text" // swipe up & down to find text and tap
ACTION_SwipeToTapTexts ActionMethod = "swipe_to_tap_texts" // swipe up & down to find text and tap
ACTION_ClosePopups ActionMethod = "close_popups"
ACTION_EndToEndDelay ActionMethod = "live_e2e"
ACTION_InstallApp ActionMethod = "install_app"
ACTION_UninstallApp ActionMethod = "uninstall_app"
ACTION_DownloadApp ActionMethod = "download_app"
)
const (
// UI validation
// selectors
SelectorName string = "ui_name"
SelectorLabel string = "ui_label"
SelectorOCR string = "ui_ocr"
SelectorImage string = "ui_image"
SelectorAI string = "ui_ai" // ui query with ai
SelectorForegroundApp string = "ui_foreground_app"
SelectorSelector string = "ui_selector"
// assertions
AssertionEqual string = "equal"
AssertionNotEqual string = "not_equal"
AssertionExists string = "exists"
AssertionNotExists string = "not_exists"
AssertionAI string = "ai_assert" // assert with ai
)
type MobileAction struct {
Method ActionMethod `json:"method,omitempty" yaml:"method,omitempty"`
Method option.ActionMethod `json:"method,omitempty" yaml:"method,omitempty"`
Params interface{} `json:"params,omitempty" yaml:"params,omitempty"`
Fn func() `json:"-" yaml:"-"` // used for function action, not serialized
Options *option.ActionOptions `json:"options,omitempty" yaml:"options,omitempty"`
@@ -102,6 +32,7 @@ func (ma MobileAction) GetOptions() []option.ActionOption {
return actionOptionList
}
// TODO: merge to uixt MCP Server
func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
actionStartTime := time.Now()
defer func() {
@@ -119,7 +50,7 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
}()
switch action.Method {
case ACTION_WebLoginNoneUI:
case option.ACTION_WebLoginNoneUI:
if len(action.Params.([]interface{})) == 4 {
driver, ok := dExt.IDriver.(*BrowserDriver)
if !ok {
@@ -129,53 +60,53 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
_, err = driver.LoginNoneUI(params[0].(string), params[1].(string), params[2].(string), params[3].(string))
return err
}
return fmt.Errorf("invalid %s params: %v", ACTION_WebLoginNoneUI, action.Params)
case ACTION_AppInstall:
return fmt.Errorf("invalid %s params: %v", option.ACTION_WebLoginNoneUI, action.Params)
case option.ACTION_AppInstall:
if app, ok := action.Params.(string); ok {
if err = dExt.GetDevice().Install(app,
option.WithRetryTimes(action.MaxRetryTimes)); err != nil {
return errors.Wrap(err, "failed to install app")
}
}
case ACTION_AppUninstall:
case option.ACTION_AppUninstall:
if packageName, ok := action.Params.(string); ok {
if err = dExt.GetDevice().Uninstall(packageName); err != nil {
return errors.Wrap(err, "failed to uninstall app")
}
}
case ACTION_AppClear:
case option.ACTION_AppClear:
if packageName, ok := action.Params.(string); ok {
if err = dExt.AppClear(packageName); err != nil {
return errors.Wrap(err, "failed to clear app")
}
}
case ACTION_AppLaunch:
case option.ACTION_AppLaunch:
if bundleId, ok := action.Params.(string); ok {
return dExt.AppLaunch(bundleId)
}
return fmt.Errorf("invalid %s params, should be bundleId(string), got %v",
ACTION_AppLaunch, action.Params)
case ACTION_SwipeToTapApp:
option.ACTION_AppLaunch, action.Params)
case option.ACTION_SwipeToTapApp:
if appName, ok := action.Params.(string); ok {
return dExt.SwipeToTapApp(appName, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params, should be app name(string), got %v",
ACTION_SwipeToTapApp, action.Params)
case ACTION_SwipeToTapText:
option.ACTION_SwipeToTapApp, action.Params)
case option.ACTION_SwipeToTapText:
if text, ok := action.Params.(string); ok {
return dExt.SwipeToTapTexts([]string{text}, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params, should be app text(string), got %v",
ACTION_SwipeToTapText, action.Params)
case ACTION_SwipeToTapTexts:
option.ACTION_SwipeToTapText, action.Params)
case option.ACTION_SwipeToTapTexts:
if texts, ok := action.Params.([]string); ok {
return dExt.SwipeToTapTexts(texts, action.GetOptions()...)
}
if texts, err := builtin.ConvertToStringSlice(action.Params); err == nil {
return dExt.SwipeToTapTexts(texts, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_SwipeToTapTexts, action.Params)
case ACTION_AppTerminate:
return fmt.Errorf("invalid %s params: %v", option.ACTION_SwipeToTapTexts, action.Params)
case option.ACTION_AppTerminate:
if bundleId, ok := action.Params.(string); ok {
success, err := dExt.AppTerminate(bundleId)
if err != nil {
@@ -187,9 +118,9 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
return nil
}
return fmt.Errorf("app_terminate params should be bundleId(string), got %v", action.Params)
case ACTION_Home:
case option.ACTION_Home:
return dExt.Home()
case ACTION_SecondaryClick:
case option.ACTION_SecondaryClick:
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
if len(params) != 2 {
return fmt.Errorf("invalid tap location params: %v", params)
@@ -197,23 +128,23 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
x, y := params[0], params[1]
return dExt.SecondaryClick(x, y)
}
return fmt.Errorf("invalid %s params: %v", ACTION_SecondaryClick, action.Params)
case ACTION_HoverBySelector:
return fmt.Errorf("invalid %s params: %v", option.ACTION_SecondaryClick, action.Params)
case option.ACTION_HoverBySelector:
if selector, ok := action.Params.(string); ok {
return dExt.HoverBySelector(selector, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_HoverBySelector, action.Params)
case ACTION_TapBySelector:
return fmt.Errorf("invalid %s params: %v", option.ACTION_HoverBySelector, action.Params)
case option.ACTION_TapBySelector:
if selector, ok := action.Params.(string); ok {
return dExt.TapBySelector(selector, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapBySelector, action.Params)
case ACTION_SecondaryClickBySelector:
return fmt.Errorf("invalid %s params: %v", option.ACTION_TapBySelector, action.Params)
case option.ACTION_SecondaryClickBySelector:
if selector, ok := action.Params.(string); ok {
return dExt.SecondaryClickBySelector(selector, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_SecondaryClickBySelector, action.Params)
case ACTION_WebCloseTab:
return fmt.Errorf("invalid %s params: %v", option.ACTION_SecondaryClickBySelector, action.Params)
case option.ACTION_WebCloseTab:
if param, ok := action.Params.(json.Number); ok {
paramInt64, _ := param.Int64()
return dExt.IDriver.(*BrowserDriver).CloseTab(int(paramInt64))
@@ -223,7 +154,7 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
return dExt.IDriver.(*BrowserDriver).CloseTab(action.Params.(int))
}
// return fmt.Errorf("invalid %s params: %v", ACTION_WebCloseTab, action.Params)
case ACTION_SetIme:
case option.ACTION_SetIme:
if ime, ok := action.Params.(string); ok {
err = dExt.SetIme(ime)
if err != nil {
@@ -231,7 +162,7 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
}
return nil
}
case ACTION_GetSource:
case option.ACTION_GetSource:
if packageName, ok := action.Params.(string); ok {
_, err = dExt.Source(option.WithProcessName(packageName))
if err != nil {
@@ -239,7 +170,7 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
}
return nil
}
case ACTION_TapXY:
case option.ACTION_TapXY:
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
// relative x,y of window size: [0.5, 0.5]
if len(params) != 2 {
@@ -248,8 +179,8 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
x, y := params[0], params[1]
return dExt.TapXY(x, y, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapXY, action.Params)
case ACTION_TapAbsXY:
return fmt.Errorf("invalid %s params: %v", option.ACTION_TapXY, action.Params)
case option.ACTION_TapAbsXY:
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
// absolute coordinates x,y of window size: [100, 300]
if len(params) != 2 {
@@ -258,19 +189,19 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
x, y := params[0], params[1]
return dExt.TapAbsXY(x, y, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapAbsXY, action.Params)
case ACTION_TapByOCR:
return fmt.Errorf("invalid %s params: %v", option.ACTION_TapAbsXY, action.Params)
case option.ACTION_TapByOCR:
if ocrText, ok := action.Params.(string); ok {
return dExt.TapByOCR(ocrText, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapByOCR, action.Params)
case ACTION_TapByCV:
return fmt.Errorf("invalid %s params: %v", option.ACTION_TapByOCR, action.Params)
case option.ACTION_TapByCV:
actionOptions := option.NewActionOptions(action.GetOptions()...)
if len(actionOptions.ScreenShotWithUITypes) > 0 {
return dExt.TapByCV(action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapByCV, action.Params)
case ACTION_DoubleTapXY:
return fmt.Errorf("invalid %s params: %v", option.ACTION_TapByCV, action.Params)
case option.ACTION_DoubleTapXY:
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
// relative x,y of window size: [0.5, 0.5]
if len(params) != 2 {
@@ -279,20 +210,20 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
x, y := params[0], params[1]
return dExt.DoubleTap(x, y)
}
return fmt.Errorf("invalid %s params: %v", ACTION_DoubleTapXY, action.Params)
case ACTION_Swipe:
return fmt.Errorf("invalid %s params: %v", option.ACTION_DoubleTapXY, action.Params)
case option.ACTION_Swipe:
params := action.Params
swipeAction := prepareSwipeAction(dExt, params, action.GetOptions()...)
return swipeAction(dExt)
case ACTION_Input:
case option.ACTION_Input:
// input text on current active element
// append \n to send text with enter
// send \b\b\b to delete 3 chars
param := fmt.Sprintf("%v", action.Params)
return dExt.Input(param)
case ACTION_Back:
case option.ACTION_Back:
return dExt.Back()
case ACTION_Sleep:
case option.ACTION_Sleep:
if param, ok := action.Params.(json.Number); ok {
seconds, _ := param.Float64()
time.Sleep(time.Duration(seconds*1000) * time.Millisecond)
@@ -315,7 +246,7 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
return nil
}
return fmt.Errorf("invalid sleep params: %v(%T)", action.Params, action.Params)
case ACTION_SleepMS:
case option.ACTION_SleepMS:
if param, ok := action.Params.(json.Number); ok {
milliseconds, _ := param.Int64()
time.Sleep(time.Duration(milliseconds) * time.Millisecond)
@@ -328,29 +259,29 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
return nil
}
return fmt.Errorf("invalid sleep ms params: %v(%T)", action.Params, action.Params)
case ACTION_SleepRandom:
case option.ACTION_SleepRandom:
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
sleepStrict(time.Now(), getSimulationDuration(params))
return nil
}
return fmt.Errorf("invalid sleep random params: %v(%T)", action.Params, action.Params)
case ACTION_ScreenShot:
case option.ACTION_ScreenShot:
// take screenshot
log.Info().Msg("take screenshot for current screen")
_, err := dExt.GetScreenResult(action.GetScreenShotOptions()...)
return err
case ACTION_ClosePopups:
case option.ACTION_ClosePopups:
return dExt.ClosePopupsHandler()
case ACTION_CallFunction:
case option.ACTION_CallFunction:
if funcDesc, ok := action.Params.(string); ok {
return dExt.Call(funcDesc, action.Fn, action.GetOptions()...)
}
return fmt.Errorf("invalid function description: %v", action.Params)
case ACTION_AIAction:
case option.ACTION_AIAction:
if prompt, ok := action.Params.(string); ok {
return dExt.AIAction(prompt, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_AIAction, action.Params)
return fmt.Errorf("invalid %s params: %v", option.ACTION_AIAction, action.Params)
default:
log.Warn().Str("action", string(action.Method)).Msg("action not implemented")
return errors.Wrapf(code.InvalidCaseError,