From 3038fb7430999f42689d752545d9040f2bc9ba7b Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Sun, 9 Feb 2025 10:51:03 +0800 Subject: [PATCH] fix: errors --- cmd/adb/install.go | 2 +- examples/uitest/bili/android/cli.go | 16 +-- examples/uitest/bili/ios/cli.go | 10 +- examples/worldcup/cli.go | 25 +++- examples/worldcup/main.go | 31 ++-- examples/worldcup/main_test.go | 8 +- internal/version/VERSION | 2 +- pkg/{ => uixt}/ai/ai.go | 0 pkg/{ => uixt}/ai/ai_test.go | 0 pkg/{ => uixt}/ai/cv.go | 39 +++-- pkg/{ => uixt}/ai/cv_vedem.go | 7 +- pkg/{ => uixt}/ai/cv_vedem_test.go | 0 pkg/{ => uixt}/ai/llm.go | 0 pkg/uixt/android_device.go | 37 ++--- pkg/uixt/android_driver_adb.go | 39 ++--- pkg/uixt/android_driver_stub.go | 9 +- pkg/uixt/android_driver_uia2.go | 56 ++++---- pkg/uixt/android_test.go | 51 +++---- pkg/uixt/demo/main_test.go | 6 +- pkg/uixt/device.go | 3 +- pkg/uixt/driver.go | 70 ++++----- pkg/uixt/driver_action.go | 9 +- pkg/uixt/driver_popups.go | 2 +- pkg/uixt/driver_screenshot.go | 51 +++---- pkg/uixt/driver_session.go | 4 +- pkg/uixt/driver_swipe.go | 8 +- pkg/uixt/driver_swipe_test.go | 16 ++- pkg/uixt/driver_tap.go | 8 +- pkg/uixt/driver_test.go | 20 +-- pkg/uixt/harmony_device.go | 112 ++++++++------- pkg/uixt/harmony_driver_hdc.go | 41 +++--- pkg/uixt/ios_device.go | 90 ++++-------- pkg/uixt/ios_driver_stub.go | 175 +++++++++++++---------- pkg/uixt/ios_driver_stub_test.go | 20 +-- pkg/uixt/ios_driver_wda.go | 85 +++++------ pkg/uixt/ios_test.go | 21 ++- pkg/uixt/option/action.go | 190 ++----------------------- pkg/uixt/option/driver.go | 49 ------- pkg/uixt/option/harmony.go | 7 + pkg/{ai => uixt/option}/screen.go | 96 ++++++------- pkg/uixt/types/app.go | 60 ++++++++ pkg/uixt/{types.go => types/device.go} | 67 +-------- pkg/uixt/types/ui.go | 10 ++ runner.go | 50 +++---- server/context.go | 6 +- server/model.go | 6 +- step_mobile_ui.go | 6 +- 47 files changed, 710 insertions(+), 910 deletions(-) rename pkg/{ => uixt}/ai/ai.go (100%) rename pkg/{ => uixt}/ai/ai_test.go (100%) rename pkg/{ => uixt}/ai/cv.go (87%) rename pkg/{ => uixt}/ai/cv_vedem.go (97%) rename pkg/{ => uixt}/ai/cv_vedem_test.go (100%) rename pkg/{ => uixt}/ai/llm.go (100%) delete mode 100644 pkg/uixt/option/driver.go rename pkg/{ai => uixt/option}/screen.go (61%) create mode 100644 pkg/uixt/types/app.go rename pkg/uixt/{types.go => types/device.go} (78%) create mode 100644 pkg/uixt/types/ui.go diff --git a/cmd/adb/install.go b/cmd/adb/install.go index 7f122245..3b94fd08 100644 --- a/cmd/adb/install.go +++ b/cmd/adb/install.go @@ -47,7 +47,7 @@ var installCmd = &cobra.Command{ return err } - err = driverExt.Install(args[0], + err = driverExt.GetDriver().GetDevice().Install(args[0], option.WithReinstall(replace), option.WithDowngrade(downgrade), option.WithGrantPermission(grant), diff --git a/examples/uitest/bili/android/cli.go b/examples/uitest/bili/android/cli.go index bcb2d0ca..53dbd254 100644 --- a/examples/uitest/bili/android/cli.go +++ b/examples/uitest/bili/android/cli.go @@ -28,24 +28,24 @@ func init() { fmt.Printf("=== start running cases, serial=%s, runTimes=%d ===\n", serial, runTimes) } -func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) { +func launchAppDriver(pkgName string) (driver uixt.IDriverExt, err error) { device, _ := uixt.NewAndroidDevice(option.WithSerialNumber(serial)) driver, err = device.NewDriver() if err != nil { return nil, err } - _, err = driver.Driver.AppTerminate(pkgName) + _, err = driver.GetDriver().AppTerminate(pkgName) if err != nil { return nil, err } - err = driver.Driver.Homescreen() + err = driver.GetDriver().Homescreen() if err != nil { return nil, err } - err = driver.Driver.AppLaunch(pkgName) + err = driver.GetDriver().AppLaunch(pkgName) if err != nil { return nil, err } @@ -67,7 +67,7 @@ func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) { return driver, nil } -func watchVideo(driver *uixt.DriverExt) (err error) { +func watchVideo(driver uixt.IDriverExt) (err error) { time.Sleep(3 * time.Second) err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2) if err != nil { @@ -96,7 +96,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) { if err != nil { // 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed) // 退出回到推荐页 - driver.Driver.PressBack() + driver.GetDriver().PressBack() return nil } @@ -104,7 +104,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) { time.Sleep(10 * time.Second) // 返回视频页面 - err = driver.Driver.PressBack() + err = driver.GetDriver().PressBack() if err != nil { return err } @@ -112,7 +112,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) { time.Sleep(1 * time.Second) // 返回推荐页 - err = driver.Driver.PressBack() + err = driver.GetDriver().PressBack() if err != nil { return err } diff --git a/examples/uitest/bili/ios/cli.go b/examples/uitest/bili/ios/cli.go index de4f1c85..5cfbffea 100644 --- a/examples/uitest/bili/ios/cli.go +++ b/examples/uitest/bili/ios/cli.go @@ -28,7 +28,7 @@ func init() { fmt.Printf("=== start running cases, serial=%s, runTimes=%d ===\n", serial, runTimes) } -func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) { +func launchAppDriver(pkgName string) (driver uixt.IDriverExt, err error) { device, _ := uixt.NewIOSDevice(option.WithUDID(serial)) driver, err = device.NewDriver() if err != nil { @@ -67,7 +67,7 @@ func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) { return driver, nil } -func watchVideo(driver *uixt.DriverExt) (err error) { +func watchVideo(driver uixt.IDriverExt) (err error) { time.Sleep(3 * time.Second) err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2) if err != nil { @@ -96,7 +96,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) { if err != nil { // 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed) // 退出回到推荐页 - driver.Driver.PressBack() + driver.GetDriver().PressBack() return nil } @@ -104,7 +104,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) { time.Sleep(10 * time.Second) // 返回视频页面 - err = driver.Driver.PressBack() + err = driver.GetDriver().PressBack() if err != nil { return err } @@ -112,7 +112,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) { time.Sleep(1 * time.Second) // 返回推荐页 - err = driver.Driver.PressBack() + err = driver.GetDriver().PressBack() if err != nil { return err } diff --git a/examples/worldcup/cli.go b/examples/worldcup/cli.go index 79891a58..8c5bd959 100644 --- a/examples/worldcup/cli.go +++ b/examples/worldcup/cli.go @@ -8,6 +8,8 @@ import ( "github.com/spf13/cobra" "github.com/httprunner/httprunner/v5/pkg/uixt" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/option" ) var rootCmd = &cobra.Command{ @@ -15,21 +17,36 @@ var rootCmd = &cobra.Command{ Short: "Monitor FIFA World Cup Live", Version: "2022.12.03.0018", RunE: func(cmd *cobra.Command, args []string) error { - var device uixt.IDevice + var driver uixt.IDriver var bundleID string if iosApp != "" { log.Info().Str("bundleID", iosApp).Msg("init ios device") - device = initIOSDevice(uuid) + device, err := uixt.NewIOSDevice(option.WithUDID(uuid)) + if err != nil { + log.Fatal().Err(err).Msg("failed to init ios device") + } + driver, _ = uixt.NewWDADriver(device) bundleID = iosApp } else if androidApp != "" { log.Info().Str("bundleID", androidApp).Msg("init android device") - device = initAndroidDevice(uuid) + device, err := uixt.NewAndroidDevice(option.WithSerialNumber(uuid)) + if err != nil { + log.Fatal().Err(err).Msg("failed to init android device") + } + driver, _ = uixt.NewADBDriver(device) bundleID = androidApp } else { return errors.New("android or ios app bundldID is required") } - wc := NewWorldCupLive(device, matchName, bundleID, duration, interval) + driverExt, err := uixt.NewDriverExt(driver, + ai.WithCVService(ai.CVServiceTypeVEDEM), + ) + if err != nil { + return err + } + + wc := NewWorldCupLive(driverExt, matchName, bundleID, duration, interval) if auto { wc.EnterLive(bundleID) diff --git a/examples/worldcup/main.go b/examples/worldcup/main.go index ee3e2726..2bf67c0e 100644 --- a/examples/worldcup/main.go +++ b/examples/worldcup/main.go @@ -37,7 +37,7 @@ func convertTimeToSeconds(timeStr string) (int, error) { return seconds, nil } -func initIOSDevice(uuid string) uixt.IDevice { +func initIOSDriver(uuid string) uixt.IDriverExt { device, err := uixt.NewIOSDevice( option.WithUDID(uuid), option.WithWDAPort(8700), option.WithWDAMjpegPort(8800), @@ -47,15 +47,17 @@ func initIOSDevice(uuid string) uixt.IDevice { if err != nil { log.Fatal().Err(err).Msg("failed to init ios device") } - return device + driver, _ := device.NewDriver() + return driver } -func initAndroidDevice(uuid string) uixt.IDevice { +func initAndroidDriver(uuid string) uixt.IDriverExt { device, err := uixt.NewAndroidDevice(option.WithSerialNumber(uuid)) if err != nil { log.Fatal().Err(err).Msg("failed to init android device") } - return device + driver, _ := device.NewDriver() + return driver } type timeLog struct { @@ -66,10 +68,9 @@ type timeLog struct { } type WorldCupLive struct { - driver *uixt.DriverExt + driver uixt.IDriverExt file *os.File resultDir string - UUID string `json:"uuid"` MatchName string `json:"matchName"` BundleID string `json:"bundleID"` StartTime string `json:"startTime"` @@ -80,12 +81,7 @@ type WorldCupLive struct { PerfFile string `json:"perf"` } -func NewWorldCupLive(device uixt.IDevice, matchName, bundleID string, duration, interval int) *WorldCupLive { - driverExt, err := device.NewDriver() - if err != nil { - log.Fatal().Err(err).Msg("failed to init driver") - } - +func NewWorldCupLive(driver uixt.IDriverExt, matchName, bundleID string, duration, interval int) *WorldCupLive { if matchName == "" { matchName = "unknown-match" } @@ -94,7 +90,7 @@ func NewWorldCupLive(device uixt.IDevice, matchName, bundleID string, duration, matchName = fmt.Sprintf("%s-%s", startTime.Format("2006-01-02"), matchName) resultDir := filepath.Join("worldcuplive", matchName, startTime.Format("15:04:05")) - if err = os.MkdirAll(filepath.Join(resultDir, "screenshot"), 0o755); err != nil { + if err := os.MkdirAll(filepath.Join(resultDir, "screenshot"), 0o755); err != nil { log.Fatal().Err(err).Msg("failed to create result dir") } @@ -104,7 +100,7 @@ func NewWorldCupLive(device uixt.IDevice, matchName, bundleID string, duration, log.Fatal().Err(err).Msg("failed to open file") } // write title - f.WriteString(fmt.Sprintf("%s\t%s\t%s\n", matchName, device.UUID(), bundleID)) + f.WriteString(fmt.Sprintf("%s\t%s\t%s\n", matchName, driver.GetDriver().GetDevice().UUID(), bundleID)) f.WriteString("utc_time\tutc_timestamp\tlive_time\tlive_seconds\n") if interval == 0 { @@ -115,10 +111,9 @@ func NewWorldCupLive(device uixt.IDevice, matchName, bundleID string, duration, } return &WorldCupLive{ - driver: driverExt, + driver: driver, file: f, resultDir: resultDir, - UUID: device.UUID(), BundleID: bundleID, Duration: duration, Interval: interval, @@ -177,13 +172,13 @@ func (wc *WorldCupLive) EnterLive(bundleID string) error { log.Info().Msg("enter world cup live") // kill app - _, err := wc.driver.Driver.AppTerminate(bundleID) + _, err := wc.driver.GetDriver().AppTerminate(bundleID) if err != nil { log.Error().Err(err).Msg("terminate app failed") } // launch app - err = wc.driver.Driver.AppLaunch(bundleID) + err = wc.driver.GetDriver().AppLaunch(bundleID) if err != nil { log.Error().Err(err).Msg("launch app failed") return err diff --git a/examples/worldcup/main_test.go b/examples/worldcup/main_test.go index 2079ac4d..f37e8878 100644 --- a/examples/worldcup/main_test.go +++ b/examples/worldcup/main_test.go @@ -33,17 +33,17 @@ func TestConvertTimeToSeconds(t *testing.T) { func TestMainIOS(t *testing.T) { uuid := "00008030-00194DA421C1802E" - device := initIOSDevice(uuid) + driver := initIOSDriver(uuid) bundleID := "com.ss.iphone.ugc.Aweme" - wc := NewWorldCupLive(device, "", bundleID, 30, 10) + wc := NewWorldCupLive(driver, "", bundleID, 30, 10) wc.EnterLive(bundleID) wc.Start() } func TestMainAndroid(t *testing.T) { - device := initAndroidDevice(uuid) + driver := initAndroidDriver(uuid) bundleID := "com.ss.android.ugc.aweme" - wc := NewWorldCupLive(device, "", bundleID, 30, 10) + wc := NewWorldCupLive(driver, "", bundleID, 30, 10) wc.EnterLive(bundleID) wc.Start() } diff --git a/internal/version/VERSION b/internal/version/VERSION index 2e105fa2..ccd3ed99 100644 --- a/internal/version/VERSION +++ b/internal/version/VERSION @@ -1 +1 @@ -v5.0.0+2502081446 +v5.0.0+2502091051 diff --git a/pkg/ai/ai.go b/pkg/uixt/ai/ai.go similarity index 100% rename from pkg/ai/ai.go rename to pkg/uixt/ai/ai.go diff --git a/pkg/ai/ai_test.go b/pkg/uixt/ai/ai_test.go similarity index 100% rename from pkg/ai/ai_test.go rename to pkg/uixt/ai/ai_test.go diff --git a/pkg/ai/cv.go b/pkg/uixt/ai/cv.go similarity index 87% rename from pkg/ai/cv.go rename to pkg/uixt/ai/cv.go index 63de25e6..2c15d246 100644 --- a/pkg/ai/cv.go +++ b/pkg/uixt/ai/cv.go @@ -9,13 +9,15 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/builtin" + "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" "github.com/pkg/errors" ) type ICVService interface { // returns CV result including ocr texts, uploaded image url, etc - ReadFromBuffer(imageBuf *bytes.Buffer, opts ...ScreenShotOption) (*CVResult, error) - ReadFromPath(imagePath string, opts ...ScreenShotOption) (*CVResult, error) + ReadFromBuffer(imageBuf *bytes.Buffer, opts ...option.ActionOption) (*CVResult, error) + ReadFromPath(imagePath string, opts ...option.ActionOption) (*CVResult, error) } type CVResult struct { @@ -75,8 +77,8 @@ type OCRText struct { Rect image.Rectangle `json:"-"` } -func (t OCRText) Size() Size { - return Size{ +func (t OCRText) Size() types.Size { + return types.Size{ Width: t.Rect.Dx(), Height: t.Rect.Dy(), } @@ -105,7 +107,7 @@ func (t OCRTexts) texts() (texts []string) { return texts } -func (t OCRTexts) FilterScope(scope AbsScope) (results OCRTexts) { +func (t OCRTexts) FilterScope(scope option.AbsScope) (results OCRTexts) { for _, ocrText := range t { rect := ocrText.Rect @@ -127,8 +129,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, opts ...ScreenFilterOption) (result OCRText, err error) { - options := NewScreenFilterOptions(opts...) +func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCRText, err error) { + options := option.NewActionOptions(opts...) var results []OCRText for _, ocrText := range t.FilterScope(options.AbsScope) { @@ -172,8 +174,8 @@ func (t OCRTexts) FindText(text string, opts ...ScreenFilterOption) (result OCRT return results[idx], nil } -func (t OCRTexts) FindTexts(texts []string, opts ...ScreenFilterOption) (results OCRTexts, err error) { - options := NewScreenFilterOptions(opts...) +func (t OCRTexts) FindTexts(texts []string, opts ...option.ActionOption) (results OCRTexts, err error) { + options := option.NewActionOptions(opts...) for _, text := range texts { ocrText, err := t.FindText(text, opts...) if err != nil { @@ -239,7 +241,7 @@ func (box Box) Center() PointF { type UIResults []UIResult -func (u UIResults) FilterScope(scope AbsScope) (results UIResults) { +func (u UIResults) FilterScope(scope option.AbsScope) (results UIResults) { for _, uiResult := range u { rect := image.Rectangle{ Min: image.Point{ @@ -267,8 +269,8 @@ func (u UIResults) FilterScope(scope AbsScope) (results UIResults) { return } -func (u UIResults) GetUIResult(opts ...ScreenFilterOption) (UIResult, error) { - options := NewScreenFilterOptions(opts...) +func (u UIResults) GetUIResult(opts ...option.ActionOption) (UIResult, error) { + options := option.NewActionOptions(opts...) uiResults := u.FilterScope(options.AbsScope) if len(uiResults) == 0 { return UIResult{}, errors.Wrap(code.CVResultNotFoundError, @@ -315,16 +317,7 @@ func (p PointF) IsIdentical(p2 PointF) bool { return math.Abs(p.X-p2.X) < 1 && math.Abs(p.Y-p2.Y) < 1 } -type Size struct { - Width int `json:"width"` - Height int `json:"height"` -} - -func (s Size) IsNil() bool { - return s.Width == 0 && s.Height == 0 -} - type Screen struct { - StatusBarSize Size `json:"statusBarSize"` - Scale float64 `json:"scale"` + StatusBarSize types.Size `json:"statusBarSize"` + Scale float64 `json:"scale"` } diff --git a/pkg/ai/cv_vedem.go b/pkg/uixt/ai/cv_vedem.go similarity index 97% rename from pkg/ai/cv_vedem.go rename to pkg/uixt/ai/cv_vedem.go index 9fb073cd..d9169a7f 100644 --- a/pkg/ai/cv_vedem.go +++ b/pkg/uixt/ai/cv_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/option" ) var client = &http.Client{ @@ -46,7 +47,7 @@ func NewVEDEMImageService() (*vedemCVService, error) { // ui - get ui position by type(s) type vedemCVService struct{} -func (s *vedemCVService) ReadFromPath(imagePath string, opts ...ScreenShotOption) ( +func (s *vedemCVService) ReadFromPath(imagePath string, opts ...option.ActionOption) ( imageResult *CVResult, err error) { imageBuf, err := os.ReadFile(imagePath) if err != nil { @@ -58,9 +59,9 @@ func (s *vedemCVService) ReadFromPath(imagePath string, opts ...ScreenShotOption return } -func (s *vedemCVService) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...ScreenShotOption) ( +func (s *vedemCVService) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...option.ActionOption) ( imageResult *CVResult, err error) { - actionOptions := NewScreenShotOptions(opts...) + actionOptions := option.NewActionOptions(opts...) screenshotActions := actionOptions.List() if len(screenshotActions) == 0 { // skip diff --git a/pkg/ai/cv_vedem_test.go b/pkg/uixt/ai/cv_vedem_test.go similarity index 100% rename from pkg/ai/cv_vedem_test.go rename to pkg/uixt/ai/cv_vedem_test.go diff --git a/pkg/ai/llm.go b/pkg/uixt/ai/llm.go similarity index 100% rename from pkg/ai/llm.go rename to pkg/uixt/ai/llm.go diff --git a/pkg/uixt/android_device.go b/pkg/uixt/android_device.go index 4479dc9a..6565163f 100644 --- a/pkg/uixt/android_device.go +++ b/pkg/uixt/android_device.go @@ -24,7 +24,9 @@ import ( "github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/pkg/gadb" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) const ( @@ -147,7 +149,7 @@ func (dev *AndroidDevice) LogEnabled() bool { return dev.LogOn } -func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) { +func (dev *AndroidDevice) NewDriver() (driverExt IDriverExt, err error) { var driver IDriver if dev.UIA2 || dev.LogOn { driver, err = NewUIA2Driver(dev) @@ -160,18 +162,21 @@ func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *Dri return nil, errors.Wrap(err, "failed to init UIA driver") } - driverExt, err = newDriverExt(dev, driver, opts...) - if err != nil { - return nil, err - } - if dev.LogOn { - err = driverExt.Driver.StartCaptureLog("hrp_adb_log") + err = driver.StartCaptureLog("hrp_adb_log") if err != nil { return nil, err } } + driverExt, err = NewDriverExt(driver, ai.WithCVService(ai.CVServiceTypeVEDEM)) + if err != nil { + return nil, errors.Wrap(err, "init android driver ext failed") + } + // setup driver + if err := driverExt.GetDriver().Setup(); err != nil { + return nil, err + } return driverExt, nil } @@ -301,17 +306,17 @@ func (dev *AndroidDevice) Uninstall(packageName string) error { return err } -func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error) { +func (dev *AndroidDevice) GetCurrentWindow() (windowInfo types.WindowInfo, err error) { // adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp' output, err := dev.RunShellCommand("dumpsys", "window", "|", "grep", "-E", "'mCurrentFocus|mFocusedApp'") if err != nil { - return WindowInfo{}, errors.Wrap(err, "get current window failed") + return types.WindowInfo{}, errors.Wrap(err, "get current window failed") } // mCurrentFocus=Window{a33bc55 u0 com.miui.home/com.miui.home.launcher.Launcher} reFocus := regexp.MustCompile(`mCurrentFocus=Window{.*? (\S+)/(\S+)}`) matches := reFocus.FindStringSubmatch(output) if len(matches) == 3 { - windowInfo = WindowInfo{ + windowInfo = types.WindowInfo{ PackageName: matches[1], Activity: matches[2], } @@ -321,7 +326,7 @@ func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error) reApp := regexp.MustCompile(`mFocusedApp=ActivityRecord{.*? (\S+)/(\S+?)\s`) matches = reApp.FindStringSubmatch(output) if len(matches) == 3 { - windowInfo = WindowInfo{ + windowInfo = types.WindowInfo{ PackageName: matches[1], Activity: matches[2], } @@ -331,24 +336,24 @@ func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error) // adb shell dumpsys activity activities | grep mResumedActivity output, err = dev.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "mResumedActivity") if err != nil { - return WindowInfo{}, errors.Wrap(err, "get current activity failed") + return types.WindowInfo{}, errors.Wrap(err, "get current activity failed") } // mResumedActivity: ActivityRecord{2db504f u0 com.miui.home/.launcher.Launcher t2} reActivity := regexp.MustCompile(`mResumedActivity: ActivityRecord{.*? (\S+)/(\S+?)\s`) matches = reActivity.FindStringSubmatch(output) if len(matches) == 3 { - windowInfo = WindowInfo{ + windowInfo = types.WindowInfo{ PackageName: matches[1], Activity: matches[2], } return windowInfo, nil } - return WindowInfo{}, errors.New("failed to extract current window") + return types.WindowInfo{}, errors.New("failed to extract current window") } -func (dev *AndroidDevice) GetPackageInfo(packageName string) (AppInfo, error) { - appInfo := AppInfo{ +func (dev *AndroidDevice) GetPackageInfo(packageName string) (types.AppInfo, error) { + appInfo := types.AppInfo{ Name: packageName, } // get package version diff --git a/pkg/uixt/android_driver_adb.go b/pkg/uixt/android_driver_adb.go index b2ab07c4..5c5e1648 100644 --- a/pkg/uixt/android_driver_adb.go +++ b/pkg/uixt/android_driver_adb.go @@ -23,8 +23,9 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/utf7" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) { @@ -86,7 +87,7 @@ func (ad *ADBDriver) DeleteSession() (err error) { return errDriverNotImplemented } -func (ad *ADBDriver) Status() (deviceStatus DeviceStatus, err error) { +func (ad *ADBDriver) Status() (deviceStatus types.DeviceStatus, err error) { err = errDriverNotImplemented return } @@ -95,22 +96,22 @@ func (ad *ADBDriver) GetDevice() IDevice { return ad.AndroidDevice } -func (ad *ADBDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) { +func (ad *ADBDriver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { err = errDriverNotImplemented return } -func (ad *ADBDriver) Location() (location Location, err error) { +func (ad *ADBDriver) Location() (location types.Location, err error) { err = errDriverNotImplemented return } -func (ad *ADBDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) { +func (ad *ADBDriver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { err = errDriverNotImplemented return } -func (ad *ADBDriver) getWindowSize() (size ai.Size, err error) { +func (ad *ADBDriver) getWindowSize() (size types.Size, err error) { // adb shell wm size output, err := ad.runShellCommand("wm", "size") if err != nil { @@ -136,14 +137,14 @@ func (ad *ADBDriver) getWindowSize() (size ai.Size, err error) { ss := strings.Split(resolution, "x") width, _ := strconv.Atoi(ss[0]) height, _ := strconv.Atoi(ss[1]) - return ai.Size{Width: width, Height: height}, nil + return types.Size{Width: width, Height: height}, nil } } err = errors.New("physical window size not found by adb") return } -func (ad *ADBDriver) WindowSize() (size ai.Size, err error) { +func (ad *ADBDriver) WindowSize() (size types.Size, err error) { if !ad.windowSize.IsNil() { // use cached window size return ad.windowSize, nil @@ -157,11 +158,11 @@ func (ad *ADBDriver) WindowSize() (size ai.Size, err error) { orientation, err2 := ad.Orientation() if err2 != nil { // Notice: do not return err if get window orientation failed - orientation = OrientationPortrait + orientation = types.OrientationPortrait log.Warn().Err(err2).Msgf( "get window orientation failed, use default %s", orientation) } - if orientation != OrientationPortrait { + if orientation != types.OrientationPortrait { size.Width, size.Height = size.Height, size.Width } @@ -243,7 +244,7 @@ func (ad *ADBDriver) StopCamera() (err error) { return } -func (ad *ADBDriver) Orientation() (orientation Orientation, err error) { +func (ad *ADBDriver) Orientation() (orientation types.Orientation, err error) { output, err := ad.runShellCommand("dumpsys", "input", "|", "grep", "'SurfaceOrientation'") if err != nil { return @@ -252,9 +253,9 @@ func (ad *ADBDriver) Orientation() (orientation Orientation, err error) { matches := re.FindStringSubmatch(output) if len(matches) > 1 { // 确保找到了匹配项 if matches[1] == "0" || matches[1] == "2" { - return OrientationPortrait, nil + return types.OrientationPortrait, nil } else if matches[1] == "1" || matches[1] == "3" { - return OrientationLandscapeLeft, nil + return types.OrientationLandscapeLeft, nil } } err = fmt.Errorf("not found SurfaceOrientation value") @@ -490,12 +491,12 @@ func (ad *ADBDriver) ForceTouchFloat(x, y, pressure float64, second ...float64) return } -func (ad *ADBDriver) SetPasteboard(contentType PasteboardType, content string) (err error) { +func (ad *ADBDriver) SetPasteboard(contentType types.PasteboardType, content string) (err error) { err = errDriverNotImplemented return } -func (ad *ADBDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) { +func (ad *ADBDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { err = errDriverNotImplemented return } @@ -618,17 +619,17 @@ func (ad *ADBDriver) Clear(packageName string) error { return nil } -func (ad *ADBDriver) PressButton(devBtn DeviceButton) (err error) { +func (ad *ADBDriver) PressButton(devBtn types.DeviceButton) (err error) { err = errDriverNotImplemented return } -func (ad *ADBDriver) Rotation() (rotation Rotation, err error) { +func (ad *ADBDriver) Rotation() (rotation types.Rotation, err error) { err = errDriverNotImplemented return } -func (ad *ADBDriver) SetRotation(rotation Rotation) (err error) { +func (ad *ADBDriver) SetRotation(rotation types.Rotation) (err error) { err = errDriverNotImplemented return } @@ -856,7 +857,7 @@ func (ad *ADBDriver) GetDriverResults() []*DriverRequests { return nil } -func (ad *ADBDriver) GetForegroundApp() (app AppInfo, err error) { +func (ad *ADBDriver) GetForegroundApp() (app types.AppInfo, err error) { packageInfo, err := ad.runShellCommand( "CLASSPATH=/data/local/tmp/evalite", "app_process", "/", "com.bytedance.iesqa.eval_process.PackageService", "2>/dev/null") diff --git a/pkg/uixt/android_driver_stub.go b/pkg/uixt/android_driver_stub.go index dd703f85..51cd09ca 100644 --- a/pkg/uixt/android_driver_stub.go +++ b/pkg/uixt/android_driver_stub.go @@ -15,6 +15,7 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) const ( @@ -166,17 +167,17 @@ func (sad *StubAndroidDriver) close() error { return nil } -func (sad *StubAndroidDriver) Status() (DeviceStatus, error) { +func (sad *StubAndroidDriver) Status() (types.DeviceStatus, error) { app, err := sad.GetForegroundApp() if err != nil { - return DeviceStatus{}, err + return types.DeviceStatus{}, err } res, err := sad.sendCommand(app.PackageName, "Hello", nil) if err != nil { - return DeviceStatus{}, err + return types.DeviceStatus{}, err } log.Info().Msg(fmt.Sprintf("ping stub result :%v", res)) - return DeviceStatus{}, nil + return types.DeviceStatus{}, nil } func (sad *StubAndroidDriver) Source(srcOpt ...option.SourceOption) (source string, err error) { diff --git a/pkg/uixt/android_driver_uia2.go b/pkg/uixt/android_driver_uia2.go index 563bcce2..b9f6e6f1 100644 --- a/pkg/uixt/android_driver_uia2.go +++ b/pkg/uixt/android_driver_uia2.go @@ -16,8 +16,8 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/utf7" - "github.com/httprunner/httprunner/v5/pkg/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) var errDriverNotImplemented = errors.New("driver method not implemented") @@ -133,12 +133,12 @@ func (ud *UIA2Driver) DeleteSession() (err error) { return err } -func (ud *UIA2Driver) Status() (deviceStatus DeviceStatus, err error) { +func (ud *UIA2Driver) Status() (deviceStatus types.DeviceStatus, err error) { // register(getHandler, new Status("/wd/hub/status")) var rawResp rawResponse // Notice: use Driver.GET instead of httpGET to avoid loop calling if rawResp, err = ud.GET("/status"); err != nil { - return DeviceStatus{Ready: false}, err + return types.DeviceStatus{Ready: false}, err } reply := new(struct { Value struct { @@ -147,34 +147,34 @@ func (ud *UIA2Driver) Status() (deviceStatus DeviceStatus, err error) { } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return DeviceStatus{Ready: false}, err + return types.DeviceStatus{Ready: false}, err } - return DeviceStatus{Ready: true}, nil + return types.DeviceStatus{Ready: true}, nil } -func (ud *UIA2Driver) DeviceInfo() (deviceInfo DeviceInfo, err error) { +func (ud *UIA2Driver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { // register(getHandler, new GetDeviceInfo("/wd/hub/session/:sessionId/appium/device/info")) var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionID, "appium/device/info"); err != nil { - return DeviceInfo{}, err + return types.DeviceInfo{}, err } - reply := new(struct{ Value struct{ DeviceInfo } }) + reply := new(struct{ Value struct{ types.DeviceInfo } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return DeviceInfo{}, err + return types.DeviceInfo{}, err } deviceInfo = reply.Value.DeviceInfo return } -func (ud *UIA2Driver) BatteryInfo() (batteryInfo BatteryInfo, err error) { +func (ud *UIA2Driver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { // register(getHandler, new GetBatteryInfo("/wd/hub/session/:sessionId/appium/device/battery_info")) var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionID, "appium/device/battery_info"); err != nil { - return BatteryInfo{}, err + return types.BatteryInfo{}, err } - reply := new(struct{ Value struct{ BatteryInfo } }) + reply := new(struct{ Value struct{ types.BatteryInfo } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return BatteryInfo{}, err + return types.BatteryInfo{}, err } if reply.Value.Level == -1 || reply.Value.Status == -1 { return reply.Value.BatteryInfo, errors.New("cannot be retrieved from the system") @@ -183,7 +183,7 @@ func (ud *UIA2Driver) BatteryInfo() (batteryInfo BatteryInfo, err error) { return } -func (ud *UIA2Driver) WindowSize() (size ai.Size, err error) { +func (ud *UIA2Driver) WindowSize() (size types.Size, err error) { // register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size")) if !ud.windowSize.IsNil() { // use cached window size @@ -192,11 +192,11 @@ func (ud *UIA2Driver) WindowSize() (size ai.Size, err error) { var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionID, "window/:windowHandle/size"); err != nil { - return ai.Size{}, errors.Wrap(err, "get window size failed by UIA2 request") + return types.Size{}, errors.Wrap(err, "get window size failed by UIA2 request") } - reply := new(struct{ Value struct{ ai.Size } }) + reply := new(struct{ Value struct{ types.Size } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return ai.Size{}, errors.Wrap(err, "get window size failed by UIA2 response") + return types.Size{}, errors.Wrap(err, "get window size failed by UIA2 response") } size = reply.Value.Size @@ -204,9 +204,9 @@ func (ud *UIA2Driver) WindowSize() (size ai.Size, err error) { orientation, err := ud.Orientation() if err != nil { log.Warn().Err(err).Msgf("window size get orientation failed, use default orientation") - orientation = OrientationPortrait + orientation = types.OrientationPortrait } - if orientation != OrientationPortrait { + if orientation != types.OrientationPortrait { size.Width, size.Height = size.Height, size.Width } @@ -244,13 +244,13 @@ func (ud *UIA2Driver) PressKeyCodes(keyCode KeyCode, metaState KeyMeta, flags .. return } -func (ud *UIA2Driver) Orientation() (orientation Orientation, err error) { +func (ud *UIA2Driver) Orientation() (orientation types.Orientation, err error) { // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)] var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionID, "/orientation"); err != nil { return "", err } - reply := new(struct{ Value Orientation }) + reply := new(struct{ Value types.Orientation }) if err = json.Unmarshal(rawResp, reply); err != nil { return "", err } @@ -418,7 +418,7 @@ func (ud *UIA2Driver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Actio return err } -func (ud *UIA2Driver) SetPasteboard(contentType PasteboardType, content string) (err error) { +func (ud *UIA2Driver) SetPasteboard(contentType types.PasteboardType, content string) (err error) { lbl := content const defaultLabelLen = 10 @@ -436,9 +436,9 @@ func (ud *UIA2Driver) SetPasteboard(contentType PasteboardType, content string) return } -func (ud *UIA2Driver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) { +func (ud *UIA2Driver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { if len(contentType) == 0 { - contentType = PasteboardTypePlaintext + contentType = types.PasteboardTypePlaintext } // register(postHandler, new GetClipboard("/wd/hub/session/:sessionId/appium/device/get_clipboard")) data := map[string]interface{}{ @@ -541,15 +541,15 @@ func (ud *UIA2Driver) Input(text string, opts ...option.ActionOption) (err error return ud.SendKeys(text, opts...) } -func (ud *UIA2Driver) Rotation() (rotation Rotation, err error) { +func (ud *UIA2Driver) Rotation() (rotation types.Rotation, err error) { // register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation")) var rawResp rawResponse if rawResp, err = ud.httpGET("/session", ud.sessionID, "rotation"); err != nil { - return Rotation{}, err + return types.Rotation{}, err } - reply := new(struct{ Value Rotation }) + reply := new(struct{ Value types.Rotation }) if err = json.Unmarshal(rawResp, reply); err != nil { - return Rotation{}, err + return types.Rotation{}, err } rotation = reply.Value diff --git a/pkg/uixt/android_test.go b/pkg/uixt/android_test.go index 862f01a0..1649dec8 100644 --- a/pkg/uixt/android_test.go +++ b/pkg/uixt/android_test.go @@ -10,9 +10,10 @@ import ( "github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) -var driverExt *DriverExt +var driverExt IDriverExt func setupAndroidAdbDriver(t *testing.T) { device, err := NewAndroidDevice() @@ -91,7 +92,7 @@ func TestDriver_DeviceSize(t *testing.T) { func TestDriver_Source(t *testing.T) { setupAndroidUIA2Driver(t) - source, err := driverExt.Driver.Source() + source, err := driverExt.GetDriver().Source() if err != nil { t.Fatal(err) } @@ -140,7 +141,7 @@ func TestDriver_DeviceInfo(t *testing.T) { func TestDriver_Tap(t *testing.T) { setupAndroidUIA2Driver(t) - driverExt.Driver.StartCaptureLog("") + driverExt.GetDriver().StartCaptureLog("") err := driverExt.TapXY(0.5, 0.5, option.WithIdentifier("test"), option.WithPressDuration(4)) @@ -149,18 +150,18 @@ func TestDriver_Tap(t *testing.T) { } //time.Sleep(time.Second) // - //err = driverExt.Driver.Tap(60.5, 125.5, WithIdentifier("test")) + //err = driverExt.GetDriver().Tap(60.5, 125.5, WithIdentifier("test")) //if err != nil { // t.Fatal(err) //} //time.Sleep(time.Second) - //result, _ := driverExt.Driver.StopCaptureLog() + //result, _ := driverExt.GetDriver().StopCaptureLog() //t.Log(result) } func TestDriver_Swipe(t *testing.T) { setupAndroidUIA2Driver(t) - err := driverExt.Driver.Swipe(400, 1000, 400, 500, + err := driverExt.GetDriver().Swipe(400, 1000, 400, 500, option.WithPressDuration(0.5)) if err != nil { t.Fatal(err) @@ -192,7 +193,7 @@ func TestDriver_Drag(t *testing.T) { func TestDriver_SendKeys(t *testing.T) { setupAndroidUIA2Driver(t) - err := driverExt.Driver.SendKeys("辽宁省沈阳市新民市民族街36-4", + err := driverExt.GetDriver().SendKeys("辽宁省沈阳市新民市民族街36-4", option.WithIdentifier("test")) if err != nil { t.Fatal(err) @@ -222,7 +223,7 @@ func TestDriver_PressBack(t *testing.T) { func TestDriver_SetRotation(t *testing.T) { // err = driver.SetRotation(Rotation{Z: 0}) - err := driver.SetRotation(Rotation{Z: 270}) + err := driver.SetRotation(types.Rotation{Z: 270}) if err != nil { t.Fatal(err) } @@ -230,10 +231,10 @@ func TestDriver_SetRotation(t *testing.T) { func TestDriver_GetOrientation(t *testing.T) { setupAndroidUIA2Driver(t) - _, _ = driverExt.Driver.AppTerminate("com.quark.browser") - _ = driverExt.Driver.AppLaunch("com.quark.browser") + _, _ = driverExt.GetDriver().AppTerminate("com.quark.browser") + _ = driverExt.GetDriver().AppLaunch("com.quark.browser") time.Sleep(2 * time.Second) - _ = driverExt.Driver.Homescreen() + _ = driverExt.GetDriver().Homescreen() } func Test_getFreePort(t *testing.T) { @@ -251,12 +252,12 @@ func TestDriver_AppLaunch(t *testing.T) { t.Fatal(err) } - err = driver.Driver.AppLaunch("com.android.settings") + err = driver.GetDriver().AppLaunch("com.android.settings") if err != nil { t.Fatal(err) } - raw, err := driver.Driver.Screenshot() + raw, err := driver.GetDriver().Screenshot() if err != nil { t.Fatal(err) } @@ -268,10 +269,10 @@ func TestDriver_IsAppInForeground(t *testing.T) { setupAndroidUIA2Driver(t) // setupAndroidAdbDriver(t) - err := driverExt.Driver.AppLaunch("com.android.settings") + err := driverExt.GetDriver().AppLaunch("com.android.settings") checkErr(t, err) - app, err := driverExt.Driver.GetForegroundApp() + app, err := driverExt.GetDriver().GetForegroundApp() checkErr(t, err) if app.PackageName != "com.android.settings" { t.FailNow() @@ -280,18 +281,18 @@ func TestDriver_IsAppInForeground(t *testing.T) { t.FailNow() } - err = driverExt.Driver.AssertForegroundApp("com.android.settings") + err = driverExt.GetDriver().AssertForegroundApp("com.android.settings") if err != nil { t.Fatal(err) } time.Sleep(2 * time.Second) - _, err = driverExt.Driver.AppTerminate("com.android.settings") + _, err = driverExt.GetDriver().AppTerminate("com.android.settings") if err != nil { t.Fatal(err) } - err = driverExt.Driver.AssertForegroundApp("com.android.settings") + err = driverExt.GetDriver().AssertForegroundApp("com.android.settings") if err == nil { t.Fatal(err) } @@ -304,19 +305,19 @@ func TestDriver_KeepAlive(t *testing.T) { t.Fatal(err) } - err = driver.Driver.AppLaunch("com.android.settings") + err = driver.GetDriver().AppLaunch("com.android.settings") if err != nil { t.Fatal(err) } - _, err = driver.Driver.Screenshot() + _, err = driver.GetDriver().Screenshot() if err != nil { t.Fatal(err) } time.Sleep(60 * time.Second) - _, err = driver.Driver.Screenshot() + _, err = driver.GetDriver().Screenshot() if err != nil { t.Fatal(err) } @@ -329,7 +330,7 @@ func TestDriver_AppTerminate(t *testing.T) { t.Fatal(err) } - _, err = driver.Driver.AppTerminate("tv.danmaku.bili") + _, err = driver.GetDriver().AppTerminate("tv.danmaku.bili") if err != nil { t.Fatal(err) } @@ -391,7 +392,7 @@ func TestTapTexts(t *testing.T) { }, }, } - err := driverExt.Driver.TapByTexts(actions...) + err := driverExt.GetDriver().TapByTexts(actions...) if err != nil { t.Fatal(err) } @@ -399,7 +400,7 @@ func TestTapTexts(t *testing.T) { func TestRecordVideo(t *testing.T) { setupAndroidAdbDriver(t) - path, err := driverExt.Driver.(*ADBDriver).RecordScreen("", 5*time.Second) + path, err := driverExt.GetDriver().(*ADBDriver).RecordScreen("", 5*time.Second) if err != nil { t.Fatal(err) } @@ -409,7 +410,7 @@ func TestRecordVideo(t *testing.T) { func Test_Android_Backspace(t *testing.T) { setupAndroidAdbDriver(t) - err := driverExt.Driver.Backspace(1) + err := driverExt.GetDriver().Backspace(1) if err != nil { t.Fatal(err) } diff --git a/pkg/uixt/demo/main_test.go b/pkg/uixt/demo/main_test.go index 41c32233..771e0036 100644 --- a/pkg/uixt/demo/main_test.go +++ b/pkg/uixt/demo/main_test.go @@ -22,16 +22,14 @@ func TestIOSDemo(t *testing.T) { t.Fatal(err) } - capabilities := option.NewCapabilities() - capabilities.WithDefaultAlertAction(option.AlertActionAccept) // or uixt.AlertActionDismiss - driverExt, err := device.NewDriver(option.WithDriverCapabilities(capabilities)) + driverExt, err := device.NewDriver() if err != nil { t.Fatal(err) } // release session defer func() { - driverExt.Driver.DeleteSession() + driverExt.GetDriver().DeleteSession() }() // 持续监测手机屏幕,直到出现青少年模式弹窗后,点击「我知道了」 diff --git a/pkg/uixt/device.go b/pkg/uixt/device.go index 89eb1f29..c1a131a9 100644 --- a/pkg/uixt/device.go +++ b/pkg/uixt/device.go @@ -2,6 +2,7 @@ package uixt import ( "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) // current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice @@ -14,7 +15,7 @@ type IDevice interface { Install(appPath string, opts ...option.InstallOption) error Uninstall(packageName string) error - GetPackageInfo(packageName string) (AppInfo, error) + GetPackageInfo(packageName string) (types.AppInfo, error) // TODO: remove? LogEnabled() bool diff --git a/pkg/uixt/driver.go b/pkg/uixt/driver.go index 6bce7132..b848aa1e 100644 --- a/pkg/uixt/driver.go +++ b/pkg/uixt/driver.go @@ -16,8 +16,9 @@ import ( "github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/json" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) var ( @@ -40,10 +41,10 @@ type IDriver interface { // GetSession returns session cache, including requests, screenshots, etc. GetSession() *Session - Status() (DeviceStatus, error) + Status() (types.DeviceStatus, error) GetDevice() IDevice - DeviceInfo() (DeviceInfo, error) + DeviceInfo() (types.DeviceInfo, error) // Location Returns device location data. // @@ -56,13 +57,13 @@ type IDriver interface { // // The return value could be zero even if the permission is set to 'Always' // since the location service needs some time to update the location data. - Location() (Location, error) - BatteryInfo() (BatteryInfo, error) + Location() (types.Location, error) + BatteryInfo() (types.BatteryInfo, error) // WindowSize Return the width and height in portrait mode. // when getting the window size in wda/ui2/adb, if the device is in landscape mode, // the width and height will be reversed. - WindowSize() (ai.Size, error) + WindowSize() (types.Size, error) Screen() (ai.Screen, error) Scale() (float64, error) @@ -78,7 +79,7 @@ type IDriver interface { // Either `true` if the app has been successfully terminated or `false` if it was not running AppTerminate(packageName string) (bool, error) // GetForegroundApp returns current foreground app package name and activity name - GetForegroundApp() (app AppInfo, err error) + GetForegroundApp() (app types.AppInfo, err error) // AssertForegroundApp returns nil if the given package and activity are in foreground AssertForegroundApp(packageName string, activityType ...string) error @@ -87,10 +88,10 @@ type IDriver interface { // StopCamera Stops the camera for recording StopCamera() error - Orientation() (orientation Orientation, err error) + Orientation() (orientation types.Orientation, err error) - SetRotation(rotation Rotation) (err error) - Rotation() (rotation Rotation, err error) + SetRotation(rotation types.Rotation) (err error) + Rotation() (rotation types.Rotation, err error) // Tap Sends a tap event at the coordinate. Tap(x, y float64, opts ...option.ActionOption) error @@ -110,10 +111,10 @@ type IDriver interface { Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error // SetPasteboard Sets data to the general pasteboard - SetPasteboard(contentType PasteboardType, content string) error + SetPasteboard(contentType types.PasteboardType, content string) error // GetPasteboard Gets the data contained in the general pasteboard. // It worked when `WDA` was foreground. https://github.com/appium/WebDriverAgent/issues/330 - GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) + GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) SetIme(ime string) error @@ -128,7 +129,7 @@ type IDriver interface { Clear(packageName string) error // PressButton Presses the corresponding hardware button on the device - PressButton(devBtn DeviceButton) error + PressButton(devBtn types.DeviceButton) error // PressBack Presses the back button PressBack(opts ...option.ActionOption) error @@ -177,6 +178,13 @@ type IDriverExt interface { GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error) + GetScreenShot(fileName string) (raw *bytes.Buffer, path string, err error) + + TapByOCR(ocrText string, opts ...option.ActionOption) error + TapXY(x, y float64, opts ...option.ActionOption) error + TapAbsXY(x, y float64, opts ...option.ActionOption) error + TapOffset(param string, xOffset, yOffset float64, opts ...option.ActionOption) (err error) + TapByUIDetection(opts ...option.ActionOption) error // swipe SwipeRelative(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error @@ -184,15 +192,25 @@ type IDriverExt interface { SwipeDown(opts ...option.ActionOption) error SwipeLeft(opts ...option.ActionOption) error SwipeRight(opts ...option.ActionOption) error + + SwipeToTapApp(appName string, opts ...option.ActionOption) error + + CheckPopup() (popup *PopupInfo, err error) + ClosePopupsHandler() error + + DoAction(action MobileAction) (err error) + DoValidation(check, assert, expected string, message ...string) (err error) } func NewDriverExt(driver IDriver, opts ...ai.AIServiceOption) (IDriverExt, error) { services := ai.NewAIService(opts...) driverExt := &DriverExt{ - Driver: driver, - ImageService: services.ImageService, + Driver: driver, + CVService: services.ICVService, + LLMService: services.ILLMService, } // create results directory + // TODO: move to setup if err := builtin.EnsureFolderExists(config.ResultsPath); err != nil { return nil, errors.Wrap(err, "create results directory failed") } @@ -203,25 +221,9 @@ func NewDriverExt(driver IDriver, opts ...ai.AIServiceOption) (IDriverExt, error } type DriverExt struct { - Driver IDriver - ImageService ai.IImageService // used to extract image data -} - -func newDriverExt(device IDevice, driver IDriver, opts ...option.DriverOption) (dExt *DriverExt, err error) { - options := option.NewDriverOptions(opts...) - - dExt = &DriverExt{ - Driver: driver, - } - - if options.WithImageService { - if dExt.ImageService, err = ai.NewVEDEMImageService(); err != nil { - return nil, err - } - } - if options.WithResultFolder { - } - return dExt, nil + Driver IDriver + CVService ai.ICVService // OCR/CV + LLMService ai.ILLMService // LLM } func (dExt *DriverExt) GetDriver() IDriver { diff --git a/pkg/uixt/driver_action.go b/pkg/uixt/driver_action.go index 6d5a9aad..3e32e2fd 100644 --- a/pkg/uixt/driver_action.go +++ b/pkg/uixt/driver_action.go @@ -13,6 +13,7 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) type ActionMethod string @@ -169,7 +170,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) { ACTION_AppLaunch, action.Params) case ACTION_SwipeToTapApp: if appName, ok := action.Params.(string); ok { - return dExt.swipeToTapApp(appName, action.GetOptions()...) + return dExt.SwipeToTapApp(appName, action.GetOptions()...) } return fmt.Errorf("invalid %s params, should be app name(string), got %v", ACTION_SwipeToTapApp, action.Params) @@ -201,7 +202,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) { return fmt.Errorf("app_terminate params should be bundleId(string), got %v", action.Params) case ACTION_SetClipboard: if text, ok := action.Params.(string); ok { - err := dExt.Driver.SetPasteboard(PasteboardTypePlaintext, text) + err := dExt.Driver.SetPasteboard(types.PasteboardTypePlaintext, text) if err != nil { return errors.Wrap(err, "failed to set clipboard") } @@ -280,7 +281,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) { return fmt.Errorf("invalid %s params: %v", ACTION_DoubleTap, action.Params) case ACTION_Swipe: params := action.Params - swipeAction := dExt.prepareSwipeAction(params, action.GetOptions()...) + swipeAction := prepareSwipeAction(dExt, params, action.GetOptions()...) return swipeAction(dExt) case ACTION_Input: // input text on current active element @@ -328,7 +329,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) { case ACTION_ScreenShot: // take screenshot log.Info().Msg("take screenshot for current screen") - _, err := dExt.GetScreenResult(action.GetOptions()...) + _, err := dExt.GetScreenResult(action.GetScreenOptions()...) return err case ACTION_StartCamera: return dExt.Driver.StartCamera() diff --git a/pkg/uixt/driver_popups.go b/pkg/uixt/driver_popups.go index 145cd431..b507b015 100644 --- a/pkg/uixt/driver_popups.go +++ b/pkg/uixt/driver_popups.go @@ -5,7 +5,7 @@ import ( "github.com/rs/zerolog/log" "github.com/httprunner/httprunner/v5/code" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" ) diff --git a/pkg/uixt/driver_screenshot.go b/pkg/uixt/driver_screenshot.go index 95911b2e..abe8e1ac 100644 --- a/pkg/uixt/driver_screenshot.go +++ b/pkg/uixt/driver_screenshot.go @@ -18,14 +18,15 @@ 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/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) type ScreenResult struct { bufSource *bytes.Buffer // raw image buffer bytes ImagePath string `json:"image_path"` // image file path - Resolution ai.Size `json:"resolution"` + Resolution types.Size `json:"resolution"` UploadedURL string `json:"uploaded_url"` // uploaded image url Texts ai.OCRTexts `json:"texts"` // dumped raw OCRTexts Icons ai.UIResultMap `json:"icons"` // CV 识别的图标 @@ -46,26 +47,26 @@ func (s *ScreenResult) FilterTextsByScope(x1, y1, x2, y2 float64) ai.OCRTexts { // GetScreenResult takes a screenshot, returns the image recognition result func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) { - actionOptions := option.NewActionOptions(opts...) + screenshotOptions := option.NewActionOptions(opts...) var fileName string - screenshotActions := actionOptions.ScreenshotActions() - if actionOptions.ScreenShotFileName != "" { - fileName = builtin.GenNameWithTimestamp("%d_" + actionOptions.ScreenShotFileName) - } else if len(screenshotActions) != 0 { - fileName = builtin.GenNameWithTimestamp("%d_" + strings.Join(screenshotActions, "_")) + optionsList := screenshotOptions.List() + if screenshotOptions.ScreenShotFileName != "" { + fileName = builtin.GenNameWithTimestamp("%d_" + screenshotOptions.ScreenShotFileName) + } else if len(optionsList) != 0 { + fileName = builtin.GenNameWithTimestamp("%d_" + strings.Join(optionsList, "_")) } else { fileName = builtin.GenNameWithTimestamp("%d_screenshot") } var bufSource *bytes.Buffer - var imageResult *ai.ImageResult + var imageResult *ai.CVResult var imagePath string - var windowSize ai.Size + var windowSize types.Size var lastErr error // get screenshot info with retry - for i := 0; i <= actionOptions.MaxRetryTimes; i++ { + for i := 0; i < 3; i++ { bufSource, imagePath, err = dExt.GetScreenShot(fileName) if err != nil { lastErr = err @@ -85,9 +86,9 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul Tags: nil, Resolution: windowSize, } - imageResult, err = dExt.ImageService.GetImageFromBuffer(bufSource, opts...) + imageResult, err = dExt.CVService.ReadFromBuffer(bufSource, opts...) if err != nil { - log.Error().Err(err).Msg("GetImageFromBuffer from ImageService failed") + log.Error().Err(err).Msg("ReadFromBuffer from ImageService failed") lastErr = err continue } @@ -107,7 +108,7 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul screenResult.UploadedURL = imageResult.URL screenResult.Icons = imageResult.UIResult - if actionOptions.ScreenShotWithClosePopups && imageResult.ClosePopupsResult != nil { + if screenshotOptions.ScreenShotWithClosePopups && imageResult.ClosePopupsResult != nil { screenResult.Popup = &PopupInfo{ ClosePopupsResult: imageResult.ClosePopupsResult, PicName: imagePath, @@ -129,8 +130,8 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul } func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error) { - actionOptions := option.NewActionOptions(opts...) - if actionOptions.ScreenShotFileName == "" { + options := option.NewActionOptions(opts...) + if options.ScreenShotFileName == "" { opts = append(opts, option.WithScreenShotFileName("get_screen_texts")) } opts = append(opts, option.WithScreenShotOCR(true), option.WithScreenShotUpload(true)) @@ -152,8 +153,8 @@ func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOpt } func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) (point ai.PointF, err error) { - actionOptions := option.NewActionOptions(opts...) - if actionOptions.ScreenShotFileName == "" { + options := option.NewActionOptions(opts...) + if options.ScreenShotFileName == "" { opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text))) } ocrTexts, err := dExt.GetScreenTexts(opts...) @@ -161,7 +162,7 @@ func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) return } - result, err := ocrTexts.FindText(text, dExt.ParseActionOptions(opts...)...) + result, err := ocrTexts.FindText(text, opts...) if err != nil { log.Warn().Msgf("FindText failed: %s", err.Error()) return @@ -174,10 +175,10 @@ func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) } func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.PointF, err error) { - actionOptions := option.NewActionOptions(opts...) - if actionOptions.ScreenShotFileName == "" { + options := option.NewActionOptions(opts...) + if options.ScreenShotFileName == "" { opts = append(opts, option.WithScreenShotFileName( - fmt.Sprintf("find_ui_result_%s", strings.Join(actionOptions.ScreenShotWithUITypes, "_")))) + fmt.Sprintf("find_ui_result_%s", strings.Join(options.ScreenShotWithUITypes, "_")))) } screenResult, err := dExt.GetScreenResult(opts...) @@ -185,14 +186,14 @@ func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.Point return } - uiResults, err := screenResult.Icons.FilterUIResults(actionOptions.ScreenShotWithUITypes) + uiResults, err := screenResult.Icons.FilterUIResults(options.ScreenShotWithUITypes) if err != nil { return } - uiResult, err := uiResults.GetUIResult(dExt.ParseActionOptions(opts...)...) + uiResult, err := uiResults.GetUIResult(opts...) point = uiResult.Center() - log.Info().Interface("text", actionOptions.ScreenShotWithUITypes). + log.Info().Interface("text", options.ScreenShotWithUITypes). Interface("point", point).Msg("FindUIResult success") return } diff --git a/pkg/uixt/driver_session.go b/pkg/uixt/driver_session.go index aa62feab..1735291e 100644 --- a/pkg/uixt/driver_session.go +++ b/pkg/uixt/driver_session.go @@ -12,7 +12,7 @@ import ( "time" "github.com/httprunner/httprunner/v5/internal/json" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) @@ -25,7 +25,7 @@ type Session struct { // cache to avoid repeated query scale float64 - windowSize ai.Size + windowSize types.Size // cache uia2/wda request and response requests []*DriverRequests diff --git a/pkg/uixt/driver_swipe.go b/pkg/uixt/driver_swipe.go index 366d11fa..51ab99a1 100644 --- a/pkg/uixt/driver_swipe.go +++ b/pkg/uixt/driver_swipe.go @@ -10,7 +10,7 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/builtin" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" ) @@ -86,7 +86,7 @@ func (dExt *DriverExt) LoopUntil(findAction, findCondition, foundAction Action, fmt.Sprintf("loop %d times, match find condition failed", maxRetryTimes)) } -func (dExt *DriverExt) prepareSwipeAction(params interface{}, opts ...option.ActionOption) func(d *DriverExt) error { +func prepareSwipeAction(dExt *DriverExt, params interface{}, opts ...option.ActionOption) func(d *DriverExt) error { actionOptions := option.NewActionOptions(opts...) var swipeDirection interface{} @@ -177,11 +177,11 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOpti return d.TapAbsXY(point.X, point.Y, opts...) } - findAction := dExt.prepareSwipeAction(nil, optionsWithoutIdentifier...) + findAction := prepareSwipeAction(dExt, nil, optionsWithoutIdentifier...) return dExt.LoopUntil(findAction, findTexts, foundTextAction, optionsWithoutIdentifier...) } -func (dExt *DriverExt) swipeToTapApp(appName string, opts ...option.ActionOption) error { +func (dExt *DriverExt) SwipeToTapApp(appName string, opts ...option.ActionOption) error { // go to home screen if err := dExt.Driver.Homescreen(); err != nil { return errors.Wrap(err, "go to home screen failed") diff --git a/pkg/uixt/driver_swipe_test.go b/pkg/uixt/driver_swipe_test.go index 1cb3e940..3b5852fa 100644 --- a/pkg/uixt/driver_swipe_test.go +++ b/pkg/uixt/driver_swipe_test.go @@ -11,28 +11,30 @@ import ( func TestAndroidSwipeAction(t *testing.T) { setupAndroidAdbDriver(t) - swipeAction := driverExt.prepareSwipeAction("up", option.WithDirection("down")) - err := swipeAction(driverExt) + dExt := driverExt.(*DriverExt) + swipeAction := prepareSwipeAction(dExt, "up", option.WithDirection("down")) + err := swipeAction(dExt) checkErr(t, err) - swipeAction = driverExt.prepareSwipeAction("up", option.WithCustomDirection(0.5, 0.5, 0.5, 0.9)) - err = swipeAction(driverExt) + swipeAction = prepareSwipeAction(dExt, "up", option.WithCustomDirection(0.5, 0.5, 0.5, 0.9)) + err = swipeAction(dExt) checkErr(t, err) } func TestAndroidSwipeToTapApp(t *testing.T) { setupAndroidAdbDriver(t) - err := driverExt.swipeToTapApp("抖音") + err := driverExt.SwipeToTapApp("抖音") checkErr(t, err) } func TestAndroidSwipeToTapTexts(t *testing.T) { setupAndroidAdbDriver(t) - err := driverExt.Driver.AppLaunch("com.ss.android.ugc.aweme") + err := driverExt.GetDriver().AppLaunch("com.ss.android.ugc.aweme") checkErr(t, err) - err = driverExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, option.WithDirection("up")) + dExt := driverExt.(*DriverExt) + err = dExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, option.WithDirection("up")) checkErr(t, err) } diff --git a/pkg/uixt/driver_tap.go b/pkg/uixt/driver_tap.go index 7f08b19d..361a90ad 100644 --- a/pkg/uixt/driver_tap.go +++ b/pkg/uixt/driver_tap.go @@ -51,11 +51,11 @@ func (dExt *DriverExt) TapByOCR(ocrText string, opts ...option.ActionOption) err } func (dExt *DriverExt) TapByUIDetection(opts ...option.ActionOption) error { - actionOptions := option.NewActionOptions(opts...) + options := option.NewActionOptions(opts...) point, err := dExt.FindUIResult(opts...) if err != nil { - if actionOptions.IgnoreNotFoundError { + if options.IgnoreNotFoundError { return nil } return err @@ -69,11 +69,11 @@ func (dExt *DriverExt) Tap(param string, opts ...option.ActionOption) error { } func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, opts ...option.ActionOption) (err error) { - actionOptions := option.NewActionOptions(opts...) + options := option.NewActionOptions(opts...) point, err := dExt.FindUIRectInUIKit(param, opts...) if err != nil { - if actionOptions.IgnoreNotFoundError { + if options.IgnoreNotFoundError { return nil } return err diff --git a/pkg/uixt/driver_test.go b/pkg/uixt/driver_test.go index 5bc1cf01..a7352c29 100644 --- a/pkg/uixt/driver_test.go +++ b/pkg/uixt/driver_test.go @@ -3,20 +3,14 @@ package uixt import ( "testing" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" ) func TestNewDriverExt(t *testing.T) { device, _ := NewAndroidDevice() var driver IDriver var err error - if device.UIA2 || device.LogOn { - driver, err = NewUIA2Driver(device) - } else if device.STUB { - driver, err = NewStubAndroidDriver(device) - } else { - driver, err = NewADBDriver(device) - } + driver, err = NewADBDriver(device) if err != nil { t.Fatal(err) } @@ -24,6 +18,12 @@ func TestNewDriverExt(t *testing.T) { driverExt, _ := NewDriverExt(driver, ai.WithCVService(ai.CVServiceTypeVEDEM)) - driverExt.GetDriver() - t.Log(driverExt) + texts, _ := driverExt.GetScreenTexts() + t.Log(texts) + + // get original dirver + driver = driverExt.GetDriver().(*ADBDriver) + + // get device + device = driver.GetDevice().(*AndroidDevice) } diff --git a/pkg/uixt/harmony_device.go b/pkg/uixt/harmony_device.go index e6eaaf2c..65224a87 100644 --- a/pkg/uixt/harmony_device.go +++ b/pkg/uixt/harmony_device.go @@ -1,19 +1,14 @@ package uixt import ( - "fmt" - "code.byted.org/iesqa/ghdc" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/httprunner/httprunner/v5/code" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" -) - -var ( - HdcServerHost = "localhost" - HdcServerPort = ghdc.HdcServerPort // 5037 + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) type HarmonyDevice struct { @@ -23,65 +18,53 @@ type HarmonyDevice struct { func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice, err error) { deviceConfig := option.NewHarmonyDeviceOptions(opts...) - deviceList, err := GetHarmonyDevices(deviceConfig.ConnectKey) + + // get all attached android devices + hdcClient, err := ghdc.NewClientWith(option.HdcServerHost, option.HdcServerPort) if err != nil { - return nil, errors.Wrap(code.DeviceConnectionError, err.Error()) + return nil, err + } + devices, err := hdcClient.DeviceList() + if err != nil { + return nil, err + } + if len(devices) == 0 { + return nil, errors.Wrapf(code.DeviceConnectionError, + "no attached harmony devices") } - if deviceConfig.ConnectKey == "" && len(deviceList) > 1 { - return nil, errors.Wrap(code.DeviceConnectionError, "more than one device connected, please specify the serial") - } - - dev := deviceList[0] + // filter device by serial + var harmonyDevice *ghdc.Device if deviceConfig.ConnectKey == "" { - selectSerial := dev.Serial() - deviceConfig.ConnectKey = selectSerial - log.Warn(). - Str("connectKey", deviceConfig.ConnectKey). - Msg("harmony ConnectKey is not specified, select the first one") + if len(devices) > 1 { + return nil, errors.Wrap(code.DeviceConnectionError, + "more than one device connected, please specify the serial") + } + harmonyDevice = &devices[0] + deviceConfig.ConnectKey = harmonyDevice.Serial() + log.Warn().Str("serial", deviceConfig.ConnectKey). + Msg("harmony ConnectKey is not specified, select the attached one") + } else { + for _, d := range devices { + if d.Serial() == deviceConfig.ConnectKey { + harmonyDevice = &d + break + } + } + if harmonyDevice == nil { + return nil, errors.Wrapf(code.DeviceConnectionError, + "harmony device %s not attached", harmonyDevice.Serial()) + } } device = &HarmonyDevice{ HarmonyDeviceOptions: deviceConfig, - Device: dev, + Device: harmonyDevice, } log.Info().Str("connectKey", device.ConnectKey).Msg("init harmony device") return device, nil } -func GetHarmonyDevices(serial ...string) (devices []*ghdc.Device, err error) { - var hdcClient ghdc.Client - if hdcClient, err = ghdc.NewClientWith(HdcServerHost, HdcServerPort); err != nil { - return nil, err - } - var deviceList []ghdc.Device - - if deviceList, err = hdcClient.DeviceList(); err != nil { - return nil, err - } - - // filter by serial - for _, d := range deviceList { - for _, s := range serial { - if s != "" && s != d.Serial() { - continue - } - devices = append(devices, &d) - } - } - - if len(devices) == 0 { - var err error - if serial == nil || (len(serial) == 1 && serial[0] == "") { - err = fmt.Errorf("no harmony device found") - } else { - err = fmt.Errorf("no harmony device found for serial %v", serial) - } - return nil, err - } - return devices, nil -} - func (dev *HarmonyDevice) Setup() error { return nil } @@ -106,7 +89,26 @@ func (dev *HarmonyDevice) Uninstall(packageName string) error { return nil } -func (dev *HarmonyDevice) GetPackageInfo(packageName string) (AppInfo, error) { +func (dev *HarmonyDevice) GetPackageInfo(packageName string) (types.AppInfo, error) { log.Warn().Msg("get package info not implemented for harmony device, skip") - return AppInfo{}, nil + return types.AppInfo{}, nil +} + +func (dev *HarmonyDevice) NewDriver() (IDriverExt, error) { + // init harmony driver + driver, err := NewHDCDriver(dev) + if err != nil { + return nil, errors.Wrap(err, "init harmony driver failed") + } + driverExt, err := NewDriverExt(driver, ai.WithCVService(ai.CVServiceTypeVEDEM)) + if err != nil { + return nil, errors.Wrap(err, "init harmony driver ext failed") + } + + // setup driver + if err := driverExt.GetDriver().Setup(); err != nil { + return nil, err + } + + return driverExt, nil } diff --git a/pkg/uixt/harmony_driver_hdc.go b/pkg/uixt/harmony_driver_hdc.go index 9d14bd75..3acacc2f 100644 --- a/pkg/uixt/harmony_driver_hdc.go +++ b/pkg/uixt/harmony_driver_hdc.go @@ -10,8 +10,9 @@ import ( "code.byted.org/iesqa/ghdc" "github.com/rs/zerolog/log" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) type HDCDriver struct { @@ -58,31 +59,31 @@ func (hd *HDCDriver) GetSession() *Session { return hd.Session } -func (hd *HDCDriver) Status() (DeviceStatus, error) { - return DeviceStatus{}, errDriverNotImplemented +func (hd *HDCDriver) Status() (types.DeviceStatus, error) { + return types.DeviceStatus{}, errDriverNotImplemented } func (hd *HDCDriver) GetDevice() IDevice { return hd.HarmonyDevice } -func (hd *HDCDriver) DeviceInfo() (DeviceInfo, error) { - return DeviceInfo{}, errDriverNotImplemented +func (hd *HDCDriver) DeviceInfo() (types.DeviceInfo, error) { + return types.DeviceInfo{}, errDriverNotImplemented } -func (hd *HDCDriver) Location() (Location, error) { - return Location{}, errDriverNotImplemented +func (hd *HDCDriver) Location() (types.Location, error) { + return types.Location{}, errDriverNotImplemented } -func (hd *HDCDriver) BatteryInfo() (BatteryInfo, error) { - return BatteryInfo{}, errDriverNotImplemented +func (hd *HDCDriver) BatteryInfo() (types.BatteryInfo, error) { + return types.BatteryInfo{}, errDriverNotImplemented } -func (hd *HDCDriver) WindowSize() (size ai.Size, err error) { +func (hd *HDCDriver) WindowSize() (size types.Size, err error) { display, err := hd.uiDriver.GetDisplaySize() if err != nil { log.Error().Err(err).Msg("failed to get window size") - return ai.Size{}, err + return types.Size{}, err } size.Width = display.Width size.Height = display.Height @@ -137,9 +138,9 @@ func (hd *HDCDriver) AppTerminate(packageName string) (bool, error) { return true, nil } -func (hd *HDCDriver) GetForegroundApp() (app AppInfo, err error) { +func (hd *HDCDriver) GetForegroundApp() (app types.AppInfo, err error) { // Todo - return AppInfo{}, errDriverNotImplemented + return types.AppInfo{}, errDriverNotImplemented } func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...string) error { @@ -155,8 +156,8 @@ func (hd *HDCDriver) StopCamera() error { return errDriverNotImplemented } -func (hd *HDCDriver) Orientation() (orientation Orientation, err error) { - return OrientationPortrait, nil +func (hd *HDCDriver) Orientation() (orientation types.Orientation, err error) { + return types.OrientationPortrait, nil } func (hd *HDCDriver) Tap(x, y float64, opts ...option.ActionOption) error { @@ -213,11 +214,11 @@ func (hd *HDCDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Action return hd.uiDriver.InjectGesture(ghdc.NewGesture().Start(ghdc.Point{X: int(fromX), Y: int(fromY)}).MoveTo(ghdc.Point{X: int(toX), Y: int(toY)}, duration)) } -func (hd *HDCDriver) SetPasteboard(contentType PasteboardType, content string) error { +func (hd *HDCDriver) SetPasteboard(contentType types.PasteboardType, content string) error { return errDriverNotImplemented } -func (hd *HDCDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) { +func (hd *HDCDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { return nil, errDriverNotImplemented } @@ -237,7 +238,7 @@ func (hd *HDCDriver) Clear(packageName string) error { return errDriverNotImplemented } -func (hd *HDCDriver) PressButton(devBtn DeviceButton) error { +func (hd *HDCDriver) PressButton(devBtn types.DeviceButton) error { return errDriverNotImplemented } @@ -343,12 +344,12 @@ func (hd *HDCDriver) TearDown() error { return nil } -func (hd *HDCDriver) Rotation() (rotation Rotation, err error) { +func (hd *HDCDriver) Rotation() (rotation types.Rotation, err error) { err = errDriverNotImplemented return } -func (hd *HDCDriver) SetRotation(rotation Rotation) (err error) { +func (hd *HDCDriver) SetRotation(rotation types.Rotation) (err error) { err = errDriverNotImplemented return } diff --git a/pkg/uixt/ios_device.go b/pkg/uixt/ios_device.go index bd7924ac..1447c764 100644 --- a/pkg/uixt/ios_device.go +++ b/pkg/uixt/ios_device.go @@ -28,7 +28,9 @@ import ( "github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/internal/builtin" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) var tunnelManager *tunnel.TunnelManager = nil @@ -207,10 +209,10 @@ func (dev *IOSDevice) LogEnabled() bool { return dev.LogOn } -func (dev *IOSDevice) getAppInfo(packageName string) (appInfo AppInfo, err error) { +func (dev *IOSDevice) getAppInfo(packageName string) (appInfo types.AppInfo, err error) { apps, err := dev.ListApps(ApplicationTypeAny) if err != nil { - return AppInfo{}, err + return types.AppInfo{}, err } for _, app := range apps { if app.CFBundleIdentifier == packageName { @@ -222,27 +224,18 @@ func (dev *IOSDevice) getAppInfo(packageName string) (appInfo AppInfo, err error return appInfo, err } } - return AppInfo{}, fmt.Errorf("not found App by bundle id: %s", packageName) + return types.AppInfo{}, fmt.Errorf("not found App by bundle id: %s", packageName) } -func (dev *IOSDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) { - options := option.NewDriverOptions() - - // init WDA driver - capabilities := options.Capabilities - if capabilities == nil { - capabilities = option.NewCapabilities() - capabilities.WithDefaultAlertAction(option.AlertActionAccept) - } - +func (dev *IOSDevice) NewDriver() (driverExt IDriverExt, err error) { var driver IDriver if dev.STUB { - driver, err = dev.NewStubDriver() + driver, err = NewStubIOSDriver(dev) if err != nil { return nil, errors.Wrap(err, "failed to init Stub driver") } } else { - driver, err = dev.NewHTTPDriver(capabilities) + driver, err := NewWDADriver(dev) if err != nil { return nil, errors.Wrap(err, "failed to init WDA driver") } @@ -264,12 +257,7 @@ func (dev *IOSDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverE } } - driverExt, err = newDriverExt(dev, driver, opts...) - if err != nil { - return nil, err - } - - settings, err := driverExt.Driver.SetAppiumSettings(map[string]interface{}{ + settings, err := driver.SetAppiumSettings(map[string]interface{}{ "snapshotMaxDepth": dev.SnapshotMaxDepth, "acceptAlertButtonSelector": dev.AcceptAlertButtonSelector, }) @@ -279,12 +267,22 @@ func (dev *IOSDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverE log.Info().Interface("appiumWDASettings", settings).Msg("set appium WDA settings") if dev.LogOn { - err = driverExt.Driver.StartCaptureLog("hrp_wda_log") + err = driver.StartCaptureLog("hrp_wda_log") if err != nil { return nil, err } } + driverExt, err = NewDriverExt(driver, ai.WithCVService(ai.CVServiceTypeVEDEM)) + if err != nil { + return nil, errors.Wrap(err, "init ios driver ext failed") + } + + // setup driver + if err := driverExt.GetDriver().Setup(); err != nil { + return nil, err + } + return driverExt, nil } @@ -587,52 +585,16 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID return wd, nil } -const ( - defaultBightInsightPort = 8000 - defaultDouyinServerPort = 32921 -) - -func (dev *IOSDevice) NewStubDriver() (driver IDriver, err error) { - localStubPort, err := builtin.GetFreePort() - if err != nil { - return nil, errors.Wrap(code.DeviceHTTPDriverError, - fmt.Sprintf("get free port failed: %v", err)) - } - - if err = dev.forward(localStubPort, defaultBightInsightPort); err != nil { - return nil, errors.Wrap(code.DeviceHTTPDriverError, - fmt.Sprintf("forward tcp port failed: %v", err)) - } - - localServerPort, err := builtin.GetFreePort() - if err != nil { - return nil, errors.Wrap(code.DeviceHTTPDriverError, - fmt.Sprintf("get free port failed: %v", err)) - } - if err = dev.forward(localServerPort, defaultDouyinServerPort); err != nil { - return nil, errors.Wrap(code.DeviceHTTPDriverError, - fmt.Sprintf("forward tcp port failed: %v", err)) - } - host := "localhost" - stubDriver, err := newStubIOSDriver( - fmt.Sprintf("http://%s:%d", host, localStubPort), - fmt.Sprintf("http://%s:%d", host, localServerPort), dev) - if err != nil { - return nil, err - } - return stubDriver, nil -} - -func (dev *IOSDevice) GetPackageInfo(packageName string) (AppInfo, error) { +func (dev *IOSDevice) GetPackageInfo(packageName string) (types.AppInfo, error) { svc, err := installationproxy.New(dev.d) if err != nil { - return AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error()) + return types.AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error()) } defer svc.Close() apps, err := svc.BrowseAllApps() if err != nil { - return AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error()) + return types.AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error()) } for _, app := range apps { @@ -640,9 +602,9 @@ func (dev *IOSDevice) GetPackageInfo(packageName string) (AppInfo, error) { continue } - appInfo := AppInfo{ + appInfo := types.AppInfo{ Name: app.CFBundleName, - AppBaseInfo: AppBaseInfo{ + AppBaseInfo: types.AppBaseInfo{ BundleId: app.CFBundleIdentifier, PackageName: app.CFBundleIdentifier, VersionName: app.CFBundleShortVersionString, @@ -654,6 +616,6 @@ func (dev *IOSDevice) GetPackageInfo(packageName string) (AppInfo, error) { log.Info().Interface("appInfo", appInfo).Msg("get package info") return appInfo, nil } - return AppInfo{}, errors.Wrap(code.DeviceAppNotInstalled, + return types.AppInfo{}, errors.Wrap(code.DeviceAppNotInstalled, fmt.Sprintf("%s not found", packageName)) } diff --git a/pkg/uixt/ios_driver_stub.go b/pkg/uixt/ios_driver_stub.go index e58faabb..b3383288 100644 --- a/pkg/uixt/ios_driver_stub.go +++ b/pkg/uixt/ios_driver_stub.go @@ -7,13 +7,58 @@ import ( "net/http" "time" + "github.com/pkg/errors" "github.com/rs/zerolog/log" - "github.com/httprunner/httprunner/v5/pkg/ai" + "github.com/httprunner/httprunner/v5/code" + "github.com/httprunner/httprunner/v5/internal/builtin" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) -type stubIOSDriver struct { +const ( + defaultBightInsightPort = 8000 + defaultDouyinServerPort = 32921 +) + +func NewStubIOSDriver(device *IOSDevice) (driver *StubIOSDriver, err error) { + localStubPort, err := builtin.GetFreePort() + if err != nil { + return nil, errors.Wrap(code.DeviceHTTPDriverError, + fmt.Sprintf("get free port failed: %v", err)) + } + + if err = device.forward(localStubPort, defaultBightInsightPort); err != nil { + return nil, errors.Wrap(code.DeviceHTTPDriverError, + fmt.Sprintf("forward tcp port failed: %v", err)) + } + + localServerPort, err := builtin.GetFreePort() + if err != nil { + return nil, errors.Wrap(code.DeviceHTTPDriverError, + fmt.Sprintf("get free port failed: %v", err)) + } + if err = device.forward(localServerPort, defaultDouyinServerPort); err != nil { + return nil, errors.Wrap(code.DeviceHTTPDriverError, + fmt.Sprintf("forward tcp port failed: %v", err)) + } + + host := "localhost" + timeout := 10 * time.Second + driver = &StubIOSDriver{} + driver.device = device + driver.bightInsightPrefix = fmt.Sprintf("http://%s:%d", host, localStubPort) + driver.serverPrefix = fmt.Sprintf("http://%s:%d", host, localServerPort) + driver.timeout = timeout + driver.client = &http.Client{ + Timeout: time.Second * 10, // 设置超时时间为 10 秒 + } + + return driver, nil +} + +type StubIOSDriver struct { *WDADriver bightInsightPrefix string @@ -22,23 +67,7 @@ type stubIOSDriver struct { device *IOSDevice } -func newStubIOSDriver(bightInsightAddr, serverAddr string, dev *IOSDevice, readTimeout ...time.Duration) (*stubIOSDriver, error) { - timeout := 10 * time.Second - if len(readTimeout) > 0 { - timeout = readTimeout[0] - } - driver := new(stubIOSDriver) - driver.device = dev - driver.bightInsightPrefix = bightInsightAddr - driver.serverPrefix = serverAddr - driver.timeout = timeout - driver.client = &http.Client{ - Timeout: time.Second * 10, // 设置超时时间为 10 秒 - } - return driver, nil -} - -func (s *stubIOSDriver) setUpWda() (err error) { +func (s *StubIOSDriver) setUpWda() (err error) { if s.WDADriver == nil { capabilities := option.NewCapabilities() capabilities.WithDefaultAlertAction(option.AlertActionAccept) @@ -53,7 +82,7 @@ func (s *stubIOSDriver) setUpWda() (err error) { } // NewSession starts a new session and returns the DriverSession. -func (s *stubIOSDriver) NewSession(capabilities option.Capabilities) (Session, error) { +func (s *StubIOSDriver) NewSession(capabilities option.Capabilities) (Session, error) { err := s.setUpWda() if err != nil { return Session{}, err @@ -64,7 +93,7 @@ func (s *stubIOSDriver) NewSession(capabilities option.Capabilities) (Session, e // DeleteSession Kills application associated with that session and removes session // 1. alertsMonitor disable // 2. testedApplicationBundleId terminate -func (s *stubIOSDriver) DeleteSession() error { +func (s *StubIOSDriver) DeleteSession() error { err := s.setUpWda() if err != nil { return err @@ -72,34 +101,34 @@ func (s *stubIOSDriver) DeleteSession() error { return s.WDADriver.DeleteSession() } -func (s *stubIOSDriver) Status() (DeviceStatus, error) { +func (s *StubIOSDriver) Status() (types.DeviceStatus, error) { err := s.setUpWda() if err != nil { - return DeviceStatus{}, err + return types.DeviceStatus{}, err } return s.WDADriver.Status() } -func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) { +func (s *StubIOSDriver) DeviceInfo() (types.DeviceInfo, error) { err := s.setUpWda() if err != nil { - return DeviceInfo{}, err + return types.DeviceInfo{}, err } return s.WDADriver.DeviceInfo() } -func (s *stubIOSDriver) Location() (Location, error) { +func (s *StubIOSDriver) Location() (types.Location, error) { err := s.setUpWda() if err != nil { - return Location{}, err + return types.Location{}, err } return s.WDADriver.Location() } -func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) { +func (s *StubIOSDriver) BatteryInfo() (types.BatteryInfo, error) { err := s.setUpWda() if err != nil { - return BatteryInfo{}, err + return types.BatteryInfo{}, err } return s.WDADriver.BatteryInfo() } @@ -107,15 +136,15 @@ func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) { // WindowSize Return the width and height in portrait mode. // when getting the window size in wda/ui2/adb, if the device is in landscape mode, // the width and height will be reversed. -func (s *stubIOSDriver) WindowSize() (ai.Size, error) { +func (s *StubIOSDriver) WindowSize() (types.Size, error) { err := s.setUpWda() if err != nil { - return ai.Size{}, err + return types.Size{}, err } return s.WDADriver.WindowSize() } -func (s *stubIOSDriver) Screen() (ai.Screen, error) { +func (s *StubIOSDriver) Screen() (ai.Screen, error) { err := s.setUpWda() if err != nil { return ai.Screen{}, err @@ -123,7 +152,7 @@ func (s *stubIOSDriver) Screen() (ai.Screen, error) { return s.WDADriver.Screen() } -func (s *stubIOSDriver) Scale() (float64, error) { +func (s *StubIOSDriver) Scale() (float64, error) { err := s.setUpWda() if err != nil { return 0, err @@ -132,7 +161,7 @@ func (s *stubIOSDriver) Scale() (float64, error) { } // Homescreen Forces the device under test to switch to the home screen -func (s *stubIOSDriver) Homescreen() error { +func (s *StubIOSDriver) Homescreen() error { err := s.setUpWda() if err != nil { return err @@ -140,7 +169,7 @@ func (s *stubIOSDriver) Homescreen() error { return s.WDADriver.Homescreen() } -func (s *stubIOSDriver) Unlock() (err error) { +func (s *StubIOSDriver) Unlock() (err error) { err = s.setUpWda() if err != nil { return err @@ -150,7 +179,7 @@ func (s *stubIOSDriver) Unlock() (err error) { // AppLaunch Launch an application with given bundle identifier in scope of current session. // !This method is only available since Xcode9 SDK -func (s *stubIOSDriver) AppLaunch(packageName string) error { +func (s *StubIOSDriver) AppLaunch(packageName string) error { err := s.setUpWda() if err != nil { return err @@ -160,7 +189,7 @@ func (s *stubIOSDriver) AppLaunch(packageName string) error { // AppTerminate Terminate an application with the given package name. // Either `true` if the app has been successfully terminated or `false` if it was not running -func (s *stubIOSDriver) AppTerminate(packageName string) (bool, error) { +func (s *StubIOSDriver) AppTerminate(packageName string) (bool, error) { err := s.setUpWda() if err != nil { return false, err @@ -169,16 +198,16 @@ func (s *stubIOSDriver) AppTerminate(packageName string) (bool, error) { } // GetForegroundApp returns current foreground app package name and activity name -func (s *stubIOSDriver) GetForegroundApp() (app AppInfo, err error) { +func (s *StubIOSDriver) GetForegroundApp() (app types.AppInfo, err error) { err = s.setUpWda() if err != nil { - return AppInfo{}, err + return types.AppInfo{}, err } return s.WDADriver.GetForegroundApp() } // StartCamera Starts a new camera for recording -func (s *stubIOSDriver) StartCamera() error { +func (s *StubIOSDriver) StartCamera() error { err := s.setUpWda() if err != nil { return err @@ -187,7 +216,7 @@ func (s *stubIOSDriver) StartCamera() error { } // StopCamera Stops the camera for recording -func (s *stubIOSDriver) StopCamera() error { +func (s *StubIOSDriver) StopCamera() error { err := s.setUpWda() if err != nil { return err @@ -195,16 +224,16 @@ func (s *stubIOSDriver) StopCamera() error { return s.WDADriver.StopCamera() } -func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) { +func (s *StubIOSDriver) Orientation() (orientation types.Orientation, err error) { err = s.setUpWda() if err != nil { - return OrientationPortrait, err + return types.OrientationPortrait, err } return s.WDADriver.Orientation() } // Tap Sends a tap event at the coordinate. -func (s *stubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error { +func (s *StubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -213,7 +242,7 @@ func (s *stubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error { } // DoubleTap Sends a double tap event at the coordinate. -func (s *stubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error { +func (s *StubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -224,7 +253,7 @@ func (s *stubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) err // 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, opts ...option.ActionOption) error { +func (s *StubIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -234,7 +263,7 @@ func (s *stubIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) // 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, opts ...option.ActionOption) error { +func (s *StubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -243,7 +272,7 @@ func (s *stubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.Acti } // SetPasteboard Sets data to the general pasteboard -func (s *stubIOSDriver) SetPasteboard(contentType PasteboardType, content string) error { +func (s *StubIOSDriver) SetPasteboard(contentType types.PasteboardType, content string) error { err := s.setUpWda() if err != nil { return err @@ -254,7 +283,7 @@ func (s *stubIOSDriver) SetPasteboard(contentType PasteboardType, content string // GetPasteboard Gets the data contained in the general pasteboard. // // It worked when `WDA` was foreground. https://github.com/appium/WebDriverAgent/issues/330 -func (s *stubIOSDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) { +func (s *StubIOSDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { err = s.setUpWda() if err != nil { return nil, err @@ -262,7 +291,7 @@ func (s *stubIOSDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Bu return s.WDADriver.GetPasteboard(contentType) } -func (s *stubIOSDriver) SetIme(ime string) error { +func (s *StubIOSDriver) SetIme(ime string) error { err := s.setUpWda() if err != nil { return err @@ -273,7 +302,7 @@ 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, opts ...option.ActionOption) error { +func (s *StubIOSDriver) SendKeys(text string, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -282,7 +311,7 @@ func (s *stubIOSDriver) SendKeys(text string, opts ...option.ActionOption) error } // Input works like SendKeys -func (s *stubIOSDriver) Input(text string, opts ...option.ActionOption) error { +func (s *StubIOSDriver) Input(text string, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -290,7 +319,7 @@ func (s *stubIOSDriver) Input(text string, opts ...option.ActionOption) error { return s.WDADriver.Input(text, opts...) } -func (s *stubIOSDriver) Clear(packageName string) error { +func (s *StubIOSDriver) Clear(packageName string) error { err := s.setUpWda() if err != nil { return err @@ -299,7 +328,7 @@ func (s *stubIOSDriver) Clear(packageName string) error { } // PressButton Presses the corresponding hardware button on the device -func (s *stubIOSDriver) PressButton(devBtn DeviceButton) error { +func (s *StubIOSDriver) PressButton(devBtn types.DeviceButton) error { err := s.setUpWda() if err != nil { return err @@ -308,7 +337,7 @@ func (s *stubIOSDriver) PressButton(devBtn DeviceButton) error { } // PressBack Presses the back button -func (s *stubIOSDriver) PressBack(opts ...option.ActionOption) error { +func (s *StubIOSDriver) PressBack(opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -316,7 +345,7 @@ func (s *stubIOSDriver) PressBack(opts ...option.ActionOption) error { return s.WDADriver.PressBack(opts...) } -func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { +func (s *StubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { err = s.setUpWda() if err != nil { return err @@ -324,7 +353,7 @@ func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { return s.WDADriver.PressKeyCode(keyCode) } -func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) { +func (s *StubIOSDriver) Screenshot() (*bytes.Buffer, error) { err := s.setUpWda() if err != nil { return nil, err @@ -345,7 +374,7 @@ func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) { //return bytes.NewBuffer(imageBytes), nil } -func (s *stubIOSDriver) TapByText(text string, opts ...option.ActionOption) error { +func (s *StubIOSDriver) TapByText(text string, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -353,7 +382,7 @@ func (s *stubIOSDriver) TapByText(text string, opts ...option.ActionOption) erro return s.WDADriver.TapByText(text, opts...) } -func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error { +func (s *StubIOSDriver) TapByTexts(actions ...TapTextAction) error { err := s.setUpWda() if err != nil { return err @@ -362,7 +391,7 @@ func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error { } // AccessibleSource Return application elements accessibility tree -func (s *stubIOSDriver) AccessibleSource() (string, error) { +func (s *StubIOSDriver) AccessibleSource() (string, error) { err := s.setUpWda() if err != nil { return "", err @@ -375,7 +404,7 @@ func (s *stubIOSDriver) AccessibleSource() (string, error) { // Checks health of XCTest by: // 1) Querying application for some elements, // 2) Triggering some device events. -func (s *stubIOSDriver) HealthCheck() error { +func (s *StubIOSDriver) HealthCheck() error { err := s.setUpWda() if err != nil { return err @@ -383,7 +412,7 @@ func (s *stubIOSDriver) HealthCheck() error { return s.WDADriver.HealthCheck() } -func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { +func (s *StubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { err := s.setUpWda() if err != nil { return nil, err @@ -391,7 +420,7 @@ func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { return s.WDADriver.GetAppiumSettings() } -func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) { +func (s *StubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) { err := s.setUpWda() if err != nil { return nil, err @@ -399,7 +428,7 @@ func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[ return s.WDADriver.SetAppiumSettings(settings) } -func (s *stubIOSDriver) IsHealthy() (bool, error) { +func (s *StubIOSDriver) IsHealthy() (bool, error) { err := s.setUpWda() if err != nil { return false, err @@ -408,7 +437,7 @@ func (s *stubIOSDriver) IsHealthy() (bool, error) { } // triggers the log capture and returns the log entries -func (s *stubIOSDriver) StartCaptureLog(identifier ...string) (err error) { +func (s *StubIOSDriver) StartCaptureLog(identifier ...string) (err error) { err = s.setUpWda() if err != nil { return err @@ -416,7 +445,7 @@ func (s *stubIOSDriver) StartCaptureLog(identifier ...string) (err error) { return s.WDADriver.StartCaptureLog(identifier...) } -func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) { +func (s *StubIOSDriver) StopCaptureLog() (result interface{}, err error) { err = s.setUpWda() if err != nil { return nil, err @@ -424,7 +453,7 @@ func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) { return s.WDADriver.StopCaptureLog() } -func (s *stubIOSDriver) GetDriverResults() []*DriverRequests { +func (s *StubIOSDriver) GetDriverResults() []*DriverRequests { err := s.setUpWda() if err != nil { return nil @@ -432,7 +461,7 @@ func (s *stubIOSDriver) GetDriverResults() []*DriverRequests { return s.WDADriver.GetDriverResults() } -func (s *stubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) { +func (s *StubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) { resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/source?format=json&onlyWeb=false", s.bightInsightPrefix), []byte{}) if err != nil { return "", err @@ -440,7 +469,7 @@ func (s *stubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) { return string(resp), nil } -func (s *stubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { +func (s *StubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { params := map[string]interface{}{ "phone": phoneNumber, } @@ -478,7 +507,7 @@ func (s *stubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, pa return info, nil } -func (s *stubIOSDriver) LogoutNoneUI(packageName string) error { +func (s *StubIOSDriver) LogoutNoneUI(packageName string) error { resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/host/loginout/", s.serverPrefix), []byte{}) if err != nil { return err @@ -497,12 +526,12 @@ func (s *stubIOSDriver) LogoutNoneUI(packageName string) error { return nil } -func (s *stubIOSDriver) TearDown() error { +func (s *StubIOSDriver) TearDown() error { s.client.CloseIdleConnections() return nil } -func (s *stubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) { +func (s *StubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) { resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/host/app/info/", s.serverPrefix), []byte{}) if err != nil { return info, err @@ -524,6 +553,6 @@ func (s *stubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, return info, nil } -func (s *stubIOSDriver) GetSession() *Session { +func (s *StubIOSDriver) GetSession() *Session { return s.Session } diff --git a/pkg/uixt/ios_driver_stub_test.go b/pkg/uixt/ios_driver_stub_test.go index 72b05432..9451db59 100644 --- a/pkg/uixt/ios_driver_stub_test.go +++ b/pkg/uixt/ios_driver_stub_test.go @@ -11,7 +11,7 @@ import ( ) var ( - iOSStubDriver IDriver + iOSStubDriver IDriverExt iOSDevice *IOSDevice ) @@ -22,56 +22,56 @@ func setupiOSStubDriver(t *testing.T) { option.WithWDAMjpegPort(8800), option.WithIOSStub(false)) checkErr(t, err) - iOSStubDriver, err = iOSDevice.NewStubDriver() + iOSStubDriver, err = iOSDevice.NewDriver() checkErr(t, err) } func TestIOSLogin(t *testing.T) { setupiOSStubDriver(t) - info, err := iOSStubDriver.LoginNoneUI("", "12342316231", "8517", "") + info, err := iOSStubDriver.GetDriver().LoginNoneUI("", "12342316231", "8517", "") checkErr(t, err) t.Log(info) } func TestIOSLogout(t *testing.T) { setupiOSStubDriver(t) - err := iOSStubDriver.LogoutNoneUI("") + err := iOSStubDriver.GetDriver().LogoutNoneUI("") checkErr(t, err) } func TestIOSIsLogin(t *testing.T) { setupiOSStubDriver(t) - err := iOSStubDriver.LogoutNoneUI("") + err := iOSStubDriver.GetDriver().LogoutNoneUI("") checkErr(t, err) } func TestIOSSource(t *testing.T) { setupiOSStubDriver(t) - source, err := iOSStubDriver.Source() + source, err := iOSStubDriver.GetDriver().Source() checkErr(t, err) t.Log(source) } func TestIOSForeground(t *testing.T) { setupiOSStubDriver(t) - app, err := iOSStubDriver.GetForegroundApp() + app, err := iOSStubDriver.GetDriver().GetForegroundApp() checkErr(t, err) t.Log(app) } func TestIOSSwipe(t *testing.T) { setupiOSStubDriver(t) - iOSStubDriver.Swipe(540, 0, 540, 1000) + iOSStubDriver.GetDriver().Swipe(540, 0, 540, 1000) } func TestIOSSave(t *testing.T) { setupiOSStubDriver(t) - raw, err := iOSStubDriver.Screenshot() + raw, err := iOSStubDriver.GetDriver().Screenshot() if err != nil { t.Fatal(err) } - source, err := iOSStubDriver.Source() + source, err := iOSStubDriver.GetDriver().Source() if err != nil { t.Fatal(err) } diff --git a/pkg/uixt/ios_driver_wda.go b/pkg/uixt/ios_driver_wda.go index 7fb34930..a0025aa6 100644 --- a/pkg/uixt/ios_driver_wda.go +++ b/pkg/uixt/ios_driver_wda.go @@ -23,8 +23,9 @@ 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/ai" + "github.com/httprunner/httprunner/v5/pkg/uixt/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) func NewWDADriver(device *IOSDevice) (*WDADriver, error) { @@ -161,16 +162,16 @@ func (wd *WDADriver) DeleteSession() (err error) { return } -func (wd *WDADriver) Status() (deviceStatus DeviceStatus, err error) { +func (wd *WDADriver) Status() (deviceStatus types.DeviceStatus, err error) { // [[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)] var rawResp rawResponse // Notice: use Driver.GET instead of httpGET to avoid loop calling if rawResp, err = wd.GET("/status"); err != nil { - return DeviceStatus{}, err + return types.DeviceStatus{}, err } - reply := new(struct{ Value struct{ DeviceStatus } }) + reply := new(struct{ Value struct{ types.DeviceStatus } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return DeviceStatus{}, err + return types.DeviceStatus{}, err } deviceStatus = reply.Value.DeviceStatus return @@ -180,51 +181,51 @@ func (wd *WDADriver) GetDevice() IDevice { return wd.IOSDevice } -func (wd *WDADriver) DeviceInfo() (deviceInfo DeviceInfo, err error) { +func (wd *WDADriver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { // [[FBRoute GET:@"/wda/device/info"] respondWithTarget:self action:@selector(handleGetDeviceInfo:)] // [[FBRoute GET:@"/wda/device/info"].withoutSession var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/device/info"); err != nil { - return DeviceInfo{}, err + return types.DeviceInfo{}, err } - reply := new(struct{ Value struct{ DeviceInfo } }) + reply := new(struct{ Value struct{ types.DeviceInfo } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return DeviceInfo{}, err + return types.DeviceInfo{}, err } deviceInfo = reply.Value.DeviceInfo return } -func (wd *WDADriver) Location() (location Location, err error) { +func (wd *WDADriver) Location() (location types.Location, err error) { // [[FBRoute GET:@"/wda/device/location"] respondWithTarget:self action:@selector(handleGetLocation:)] // [[FBRoute GET:@"/wda/device/location"].withoutSession var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/device/location"); err != nil { - return Location{}, err + return types.Location{}, err } - reply := new(struct{ Value struct{ Location } }) + reply := new(struct{ Value struct{ types.Location } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return Location{}, err + return types.Location{}, err } location = reply.Value.Location return } -func (wd *WDADriver) BatteryInfo() (batteryInfo BatteryInfo, err error) { +func (wd *WDADriver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { // [[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)] var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/batteryInfo"); err != nil { - return BatteryInfo{}, err + return types.BatteryInfo{}, err } - reply := new(struct{ Value struct{ BatteryInfo } }) + reply := new(struct{ Value struct{ types.BatteryInfo } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return BatteryInfo{}, err + return types.BatteryInfo{}, err } batteryInfo = reply.Value.BatteryInfo return } -func (wd *WDADriver) WindowSize() (size ai.Size, err error) { +func (wd *WDADriver) WindowSize() (size types.Size, err error) { // [[FBRoute GET:@"/window/size"] respondWithTarget:self action:@selector(handleGetWindowSize:)] if !wd.windowSize.IsNil() { // use cached window size @@ -233,16 +234,16 @@ func (wd *WDADriver) WindowSize() (size ai.Size, err error) { var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/window/size"); err != nil { - return ai.Size{}, errors.Wrap(err, "get window size failed by WDA request") + return types.Size{}, errors.Wrap(err, "get window size failed by WDA request") } - reply := new(struct{ Value struct{ ai.Size } }) + reply := new(struct{ Value struct{ types.Size } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return ai.Size{}, errors.Wrap(err, "get window size failed by WDA response") + return types.Size{}, errors.Wrap(err, "get window size failed by WDA response") } size = reply.Value.Size scale, err := wd.Scale() if err != nil { - return ai.Size{}, errors.Wrap(err, "get window size scale failed") + return types.Size{}, errors.Wrap(err, "get window size scale failed") } size.Height = size.Height * int(scale) size.Width = size.Width * int(scale) @@ -281,28 +282,28 @@ func (wd *WDADriver) toScale(x float64) float64 { return x / wd.scale } -func (wd *WDADriver) ActiveAppInfo() (info AppInfo, err error) { +func (wd *WDADriver) ActiveAppInfo() (info types.AppInfo, err error) { // [[FBRoute GET:@"/wda/activeAppInfo"] respondWithTarget:self action:@selector(handleActiveAppInfo:)] // [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/activeAppInfo"); err != nil { - return AppInfo{}, err + return types.AppInfo{}, err } - reply := new(struct{ Value struct{ AppInfo } }) + reply := new(struct{ Value struct{ types.AppInfo } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return AppInfo{}, err + return types.AppInfo{}, err } info = reply.Value.AppInfo return } -func (wd *WDADriver) ActiveAppsList() (appsList []AppBaseInfo, err error) { +func (wd *WDADriver) ActiveAppsList() (appsList []types.AppBaseInfo, err error) { // [[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)] var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil { return nil, err } - reply := new(struct{ Value []AppBaseInfo }) + reply := new(struct{ Value []types.AppBaseInfo }) if err = json.Unmarshal(rawResp, reply); err != nil { return nil, err } @@ -310,14 +311,14 @@ func (wd *WDADriver) ActiveAppsList() (appsList []AppBaseInfo, err error) { return } -func (wd *WDADriver) AppState(bundleId string) (runState AppState, err error) { +func (wd *WDADriver) AppState(bundleId string) (runState types.AppState, err error) { // [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)] data := map[string]interface{}{"bundleId": bundleId} var rawResp rawResponse if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/state"); err != nil { return 0, err } - reply := new(struct{ Value AppState }) + reply := new(struct{ Value types.AppState }) if err = json.Unmarshal(rawResp, reply); err != nil { return 0, err } @@ -471,7 +472,7 @@ func (wd *WDADriver) AppDeactivate(second float64) (err error) { return } -func (wd *WDADriver) GetForegroundApp() (appInfo AppInfo, err error) { +func (wd *WDADriver) GetForegroundApp() (appInfo types.AppInfo, err error) { activeAppInfo, err := wd.ActiveAppInfo() appInfo.BundleId = activeAppInfo.BundleId if err != nil { @@ -611,7 +612,7 @@ func (wd *WDADriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Action return wd.Drag(fromX, fromY, toX, toY, opts...) } -func (wd *WDADriver) SetPasteboard(contentType PasteboardType, content string) (err error) { +func (wd *WDADriver) SetPasteboard(contentType types.PasteboardType, content string) (err error) { // [[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)] data := map[string]interface{}{ "contentType": contentType, @@ -621,7 +622,7 @@ func (wd *WDADriver) SetPasteboard(contentType PasteboardType, content string) ( return } -func (wd *WDADriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) { +func (wd *WDADriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { // [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)] data := map[string]interface{}{"contentType": contentType} var rawResp rawResponse @@ -713,7 +714,7 @@ func (wd *WDADriver) PressBack(opts ...option.ActionOption) (err error) { return } -func (wd *WDADriver) PressButton(devBtn DeviceButton) (err error) { +func (wd *WDADriver) PressButton(devBtn types.DeviceButton) (err error) { // [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)] data := map[string]interface{}{"name": devBtn} _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton") @@ -745,13 +746,13 @@ func (wd *WDADriver) StopCamera() (err error) { return nil } -func (wd *WDADriver) Orientation() (orientation Orientation, err error) { +func (wd *WDADriver) Orientation() (orientation types.Orientation, err error) { // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)] var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil { return "", err } - reply := new(struct{ Value Orientation }) + reply := new(struct{ Value types.Orientation }) if err = json.Unmarshal(rawResp, reply); err != nil { return "", err } @@ -759,28 +760,28 @@ func (wd *WDADriver) Orientation() (orientation Orientation, err error) { return } -func (wd *WDADriver) SetOrientation(orientation Orientation) (err error) { +func (wd *WDADriver) SetOrientation(orientation types.Orientation) (err error) { // [[FBRoute POST:@"/orientation"] respondWithTarget:self action:@selector(handleSetOrientation:)] data := map[string]interface{}{"orientation": orientation} _, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation") return } -func (wd *WDADriver) Rotation() (rotation Rotation, err error) { +func (wd *WDADriver) Rotation() (rotation types.Rotation, err error) { // [[FBRoute GET:@"/rotation"] respondWithTarget:self action:@selector(handleGetRotation:)] var rawResp rawResponse if rawResp, err = wd.httpGET("/session", wd.sessionID, "/rotation"); err != nil { - return Rotation{}, err + return types.Rotation{}, err } - reply := new(struct{ Value Rotation }) + reply := new(struct{ Value types.Rotation }) if err = json.Unmarshal(rawResp, reply); err != nil { - return Rotation{}, err + return types.Rotation{}, err } rotation = reply.Value return } -func (wd *WDADriver) SetRotation(rotation Rotation) (err error) { +func (wd *WDADriver) SetRotation(rotation types.Rotation) (err error) { // [[FBRoute POST:@"/rotation"] respondWithTarget:self action:@selector(handleSetRotation:)] _, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation") return diff --git a/pkg/uixt/ios_test.go b/pkg/uixt/ios_test.go index ea0b1f62..c6c953fb 100644 --- a/pkg/uixt/ios_test.go +++ b/pkg/uixt/ios_test.go @@ -11,12 +11,13 @@ import ( "github.com/rs/zerolog/log" "github.com/httprunner/httprunner/v5/pkg/uixt/option" + "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) var ( bundleId = "com.apple.Preferences" driver IDriver - iOSDriverExt *DriverExt + iOSDriverExt IDriverExt ) func setup(t *testing.T) { @@ -29,11 +30,7 @@ func setup(t *testing.T) { } capabilities := option.NewCapabilities() capabilities.WithDefaultAlertAction(option.AlertActionAccept) - driver, err = device.NewHTTPDriver(capabilities) - if err != nil { - t.Fatal(err) - } - iOSDriverExt, err = newDriverExt(device, driver) + iOSDriverExt, err = device.NewDriver() if err != nil { t.Fatal(err) } @@ -46,7 +43,7 @@ func TestViaUSB(t *testing.T) { func TestInstall(t *testing.T) { setup(t) - err := iOSDriverExt.Install("xxx.ipa", + err := iOSDriverExt.GetDriver().GetDevice().Install("xxx.ipa", option.WithRetryTimes(5)) log.Error().Err(err) if err != nil { @@ -320,7 +317,7 @@ func Test_remoteWD_SetPasteboard(t *testing.T) { setup(t) // err := driver.SetPasteboard(PasteboardTypePlaintext, "gwda") - err := driver.SetPasteboard(PasteboardTypeUrl, "Clock-stopwatch://") + err := driver.SetPasteboard(types.PasteboardTypeUrl, "Clock-stopwatch://") // userHomeDir, _ := os.UserHomeDir() // bytesImg, _ := ioutil.ReadFile(userHomeDir + "/Pictures/IMG_0806.jpg") // err := driver.SetPasteboard(PasteboardTypeImage, string(bytesImg)) @@ -335,7 +332,7 @@ func Test_remoteWD_GetPasteboard(t *testing.T) { var buffer *bytes.Buffer var err error - buffer, err = driver.GetPasteboard(PasteboardTypePlaintext) + buffer, err = driver.GetPasteboard(types.PasteboardTypePlaintext) // buffer, err = driver.GetPasteboard(PasteboardTypeUrl) if err != nil { t.Fatal(err) @@ -367,17 +364,17 @@ func Test_remoteWD_SendKeys(t *testing.T) { func Test_remoteWD_PressButton(t *testing.T) { setup(t) - err := driver.PressButton(DeviceButtonVolumeUp) + err := driver.PressButton(types.DeviceButtonVolumeUp) if err != nil { t.Fatal(err) } time.Sleep(time.Second * 1) - err = driver.PressButton(DeviceButtonVolumeDown) + err = driver.PressButton(types.DeviceButtonVolumeDown) if err != nil { t.Fatal(err) } time.Sleep(time.Second * 1) - err = driver.PressButton(DeviceButtonHome) + err = driver.PressButton(types.DeviceButtonHome) if err != nil { t.Fatal(err) } diff --git a/pkg/uixt/option/action.go b/pkg/uixt/option/action.go index 5a99a73c..24cf2acd 100644 --- a/pkg/uixt/option/action.go +++ b/pkg/uixt/option/action.go @@ -6,54 +6,24 @@ import ( "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"` + MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times + 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 + ScreenOptions // 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 { @@ -150,57 +120,11 @@ func (o *ActionOptions) Options() []ActionOption { } } - // 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) GetScreenOptions() []ActionOption { + return o.ScreenOptions.Options() } func (o *ActionOptions) GetRandomOffset() float64 { @@ -283,12 +207,6 @@ func WithIdentifier(identifier string) ActionOption { } } -func WithIndex(index int) ActionOption { - return func(o *ActionOptions) { - o.Index = index - } -} - // set alias for compatibility var WithWaitTime = WithInterval @@ -330,32 +248,6 @@ func WithCustomDirection(sx, sy, ex, ey float64) ActionOption { } } -// 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) { @@ -369,18 +261,6 @@ func WithOffsetRandomRange(min, max int) ActionOption { } } -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 @@ -404,51 +284,3 @@ func WithIgnoreNotFoundError(ignoreError bool) ActionOption { 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/option/driver.go b/pkg/uixt/option/driver.go deleted file mode 100644 index de800ab3..00000000 --- a/pkg/uixt/option/driver.go +++ /dev/null @@ -1,49 +0,0 @@ -package option - -import ( - "github.com/httprunner/funplugin" -) - -type DriverOptions struct { - Capabilities Capabilities - Plugin funplugin.IPlugin - WithImageService bool - WithResultFolder bool -} - -func NewDriverOptions(opts ...DriverOption) *DriverOptions { - driverOptions := &DriverOptions{ - WithImageService: true, - WithResultFolder: true, - } - for _, option := range opts { - option(driverOptions) - } - return driverOptions -} - -type DriverOption func(*DriverOptions) - -func WithDriverCapabilities(capabilities Capabilities) DriverOption { - return func(options *DriverOptions) { - options.Capabilities = capabilities - } -} - -func WithDriverImageService(withImageService bool) DriverOption { - return func(options *DriverOptions) { - options.WithImageService = withImageService - } -} - -func WithDriverResultFolder(withResultFolder bool) DriverOption { - return func(options *DriverOptions) { - options.WithResultFolder = withResultFolder - } -} - -func WithDriverPlugin(plugin funplugin.IPlugin) DriverOption { - return func(options *DriverOptions) { - options.Plugin = plugin - } -} diff --git a/pkg/uixt/option/harmony.go b/pkg/uixt/option/harmony.go index 0998a41f..ceaddf19 100644 --- a/pkg/uixt/option/harmony.go +++ b/pkg/uixt/option/harmony.go @@ -1,5 +1,12 @@ package option +import "code.byted.org/iesqa/ghdc" + +const ( + HdcServerHost = "localhost" + HdcServerPort = ghdc.HdcServerPort // 5037 +) + type HarmonyDeviceOptions struct { ConnectKey string `json:"connect_key,omitempty" yaml:"connect_key,omitempty"` LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"` diff --git a/pkg/ai/screen.go b/pkg/uixt/option/screen.go similarity index 61% rename from pkg/ai/screen.go rename to pkg/uixt/option/screen.go index 483362c3..3e30c7ec 100644 --- a/pkg/ai/screen.go +++ b/pkg/uixt/option/screen.go @@ -1,11 +1,10 @@ -package ai +package option -func NewScreenShotOptions(opts ...ScreenShotOption) *ScreenShotOptions { - options := &ScreenShotOptions{} - for _, option := range opts { - option(options) - } - return options +import "github.com/httprunner/httprunner/v5/pkg/uixt/types" + +type ScreenOptions struct { + ScreenShotOptions + ScreenFilterOptions } type ScreenShotOptions struct { @@ -19,8 +18,8 @@ type ScreenShotOptions struct { ScreenShotFileName string `json:"screenshot_file_name,omitempty" yaml:"screenshot_file_name,omitempty"` } -func (o *ScreenShotOptions) Options() []ScreenShotOption { - options := make([]ScreenShotOption, 0) +func (o *ScreenShotOptions) Options() []ActionOption { + options := make([]ActionOption, 0) if o == nil { return options } @@ -78,52 +77,50 @@ func (o *ScreenShotOptions) List() []string { return options } -type ScreenShotOption func(o *ScreenShotOptions) - -func WithScreenShotOCR(ocrOn bool) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotOCR(ocrOn bool) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithOCR = ocrOn } } -func WithScreenShotUpload(uploadOn bool) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotUpload(uploadOn bool) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithUpload = uploadOn } } -func WithScreenShotLiveType(liveTypeOn bool) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotLiveType(liveTypeOn bool) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithLiveType = liveTypeOn } } -func WithScreenShotLivePopularity(livePopularityOn bool) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotLivePopularity(livePopularityOn bool) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithLivePopularity = livePopularityOn } } -func WithScreenShotUITypes(uiTypes ...string) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotUITypes(uiTypes ...string) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithUITypes = uiTypes } } -func WithScreenShotClosePopups(closeOn bool) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotClosePopups(closeOn bool) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithClosePopups = closeOn } } -func WithScreenOCRCluster(ocrCluster string) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenOCRCluster(ocrCluster string) ActionOption { + return func(o *ActionOptions) { o.ScreenShotWithOCRCluster = ocrCluster } } -func WithScreenShotFileName(fileName string) ScreenShotOption { - return func(o *ScreenShotOptions) { +func WithScreenShotFileName(fileName string) ActionOption { + return func(o *ActionOptions) { o.ScreenShotFileName = fileName } } @@ -132,7 +129,7 @@ func WithScreenShotFileName(fileName string) ScreenShotOption { // [x1, y1, x2, y2] in percentage of the screen type Scope []float64 -func (s Scope) ToAbs(windowSize Size) AbsScope { +func (s Scope) ToAbs(windowSize types.Size) AbsScope { x1, y1, x2, y2 := s[0], s[1], s[2], s[3] // convert relative scope to absolute scope absX1 := int(x1 * float64(windowSize.Width)) @@ -145,12 +142,12 @@ func (s Scope) ToAbs(windowSize Size) AbsScope { // [x1, y1, x2, y2] in absolute pixels type AbsScope []int -func (s AbsScope) Option() ScreenFilterOption { +func (s AbsScope) Option() ActionOption { return WithAbsScope(s[0], s[1], s[2], s[3]) } -func NewScreenFilterOptions(opts ...ScreenFilterOption) *ScreenFilterOptions { - options := &ScreenFilterOptions{} +func NewScreenFilterOptions(opts ...ActionOption) *ActionOptions { + options := &ActionOptions{} for _, option := range opts { option(options) } @@ -162,52 +159,51 @@ type ScreenFilterOptions struct { 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 + 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"` + IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found // match one of the targets if existed } -type ScreenFilterOption func(o *ScreenFilterOptions) - // 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) ScreenFilterOption { - return func(o *ScreenFilterOptions) { +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) ScreenFilterOption { - return func(o *ScreenFilterOptions) { +func WithAbsScope(x1, y1, x2, y2 int) ActionOption { + return func(o *ActionOptions) { o.AbsScope = AbsScope{x1, y1, x2, y2} } } // tap [x, y] with offset [offsetX, offsetY] -func WithTapOffset(offsetX, offsetY int) ScreenFilterOption { - return func(o *ScreenFilterOptions) { +func WithTapOffset(offsetX, offsetY int) ActionOption { + return func(o *ActionOptions) { o.Offset = []int{offsetX, offsetY} } } -func WithRegex(regex bool) ScreenFilterOption { - return func(o *ScreenFilterOptions) { +func WithRegex(regex bool) ActionOption { + return func(o *ActionOptions) { o.Regex = regex } } -func WithMatchOne(matchOne bool) ScreenFilterOption { - return func(o *ScreenFilterOptions) { +func WithMatchOne(matchOne bool) ActionOption { + return func(o *ActionOptions) { o.MatchOne = matchOne } } -func WithIndex(index int) ScreenFilterOption { - return func(o *ScreenFilterOptions) { +func WithIndex(index int) ActionOption { + return func(o *ActionOptions) { o.Index = index } } diff --git a/pkg/uixt/types/app.go b/pkg/uixt/types/app.go new file mode 100644 index 00000000..c7d0e5d8 --- /dev/null +++ b/pkg/uixt/types/app.go @@ -0,0 +1,60 @@ +package types + +type AppInfo struct { + Name string `json:"name,omitempty"` + AppBaseInfo +} + +type WindowInfo struct { + PackageName string `json:"packageName,omitempty"` + Activity string `json:"activity,omitempty"` +} + +type AppBaseInfo struct { + Pid int `json:"pid,omitempty"` + BundleId string `json:"bundleId,omitempty"` // ios package name + ViewController string `json:"viewController,omitempty"` // ios view controller + PackageName string `json:"packageName,omitempty"` // android package name + Activity string `json:"activity,omitempty"` // android activity + VersionName string `json:"versionName,omitempty"` + VersionCode interface{} `json:"versionCode,omitempty"` // int or string + AppName string `json:"appName,omitempty"` + AppPath string `json:"appPath,omitempty"` + AppMD5 string `json:"appMD5,omitempty"` + // AppIcon string `json:"appIcon,omitempty"` +} + +type AppState int + +const ( + AppStateNotRunning AppState = 1 << iota + AppStateRunningBack + AppStateRunningFront +) + +func (v AppState) String() string { + switch v { + case AppStateNotRunning: + return "Not Running" + case AppStateRunningBack: + return "Running (Back)" + case AppStateRunningFront: + return "Running (Front)" + default: + return "UNKNOWN" + } +} + +// PasteboardType The type of the item on the pasteboard. +type PasteboardType string + +const ( + PasteboardTypePlaintext PasteboardType = "plaintext" + PasteboardTypeImage PasteboardType = "image" + PasteboardTypeUrl PasteboardType = "url" +) + +const ( + TextBackspace string = "\u0008" + TextDelete string = "\u007F" +) diff --git a/pkg/uixt/types.go b/pkg/uixt/types/device.go similarity index 78% rename from pkg/uixt/types.go rename to pkg/uixt/types/device.go index 0b26886d..5d710c74 100644 --- a/pkg/uixt/types.go +++ b/pkg/uixt/types/device.go @@ -1,8 +1,6 @@ -package uixt +package types -import ( - "fmt" -) +import "fmt" type DeviceStatus struct { Message string `json:"message"` @@ -152,65 +150,6 @@ func (bs BatteryStatus) String() string { } } -type AppInfo struct { - Name string `json:"name,omitempty"` - AppBaseInfo -} - -type WindowInfo struct { - PackageName string `json:"packageName,omitempty"` - Activity string `json:"activity,omitempty"` -} - -type AppBaseInfo struct { - Pid int `json:"pid,omitempty"` - BundleId string `json:"bundleId,omitempty"` // ios package name - ViewController string `json:"viewController,omitempty"` // ios view controller - PackageName string `json:"packageName,omitempty"` // android package name - Activity string `json:"activity,omitempty"` // android activity - VersionName string `json:"versionName,omitempty"` - VersionCode interface{} `json:"versionCode,omitempty"` // int or string - AppName string `json:"appName,omitempty"` - AppPath string `json:"appPath,omitempty"` - AppMD5 string `json:"appMD5,omitempty"` - // AppIcon string `json:"appIcon,omitempty"` -} - -type AppState int - -const ( - AppStateNotRunning AppState = 1 << iota - AppStateRunningBack - AppStateRunningFront -) - -func (v AppState) String() string { - switch v { - case AppStateNotRunning: - return "Not Running" - case AppStateRunningBack: - return "Running (Back)" - case AppStateRunningFront: - return "Running (Front)" - default: - return "UNKNOWN" - } -} - -// PasteboardType The type of the item on the pasteboard. -type PasteboardType string - -const ( - PasteboardTypePlaintext PasteboardType = "plaintext" - PasteboardTypeImage PasteboardType = "image" - PasteboardTypeUrl PasteboardType = "url" -) - -const ( - TextBackspace string = "\u0008" - TextDelete string = "\u007F" -) - // DeviceButton A physical button on an iOS device. type DeviceButton string @@ -249,8 +188,6 @@ type Rotation struct { Z int `json:"z"` } -type Condition func(wd IDriver) (bool, error) - type Direction string const ( diff --git a/pkg/uixt/types/ui.go b/pkg/uixt/types/ui.go new file mode 100644 index 00000000..78c61f28 --- /dev/null +++ b/pkg/uixt/types/ui.go @@ -0,0 +1,10 @@ +package types + +type Size struct { + Width int `json:"width"` + Height int `json:"height"` +} + +func (s Size) IsNil() bool { + return s.Width == 0 && s.Height == 0 +} diff --git a/runner.go b/runner.go index 2df5a19e..bee5c3f9 100644 --- a/runner.go +++ b/runner.go @@ -235,7 +235,7 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) { // release UI driver session defer func() { for _, client := range caseRunner.uixtDrivers { - client.Driver.DeleteSession() + client.GetDriver().DeleteSession() } }() @@ -281,7 +281,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) { TestCase: testcase, hrpRunner: r, parser: newParser(), - uixtDrivers: make(map[string]*uixt.DriverExt), + uixtDrivers: make(map[string]uixt.IDriverExt), } config := testcase.Config.Get() @@ -336,7 +336,7 @@ type CaseRunner struct { parametersIterator *ParametersIterator // UI automation clients for iOS and Android, key is udid/serial - uixtDrivers map[string]*uixt.DriverExt + uixtDrivers map[string]uixt.IDriverExt } func (r *CaseRunner) GetParametersIterator() *ParametersIterator { @@ -428,16 +428,14 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) { return nil, errors.Wrap(err, "init android device failed") } if err := device.Setup(); err != nil { - return nil, err + return nil, errors.Wrap(err, "setup android device failed") } - driver, err := device.NewDriver() + driverExt, err := device.NewDriver() if err != nil { - return nil, err + return nil, errors.Wrap(err, "init android driver failed") } - if err := driver.Setup(); err != nil { - return nil, err - } - r.uixtDrivers[device.SerialNumber] = driver + + r.uixtDrivers[androidDevice.SerialNumber] = driverExt } // parse iOS devices config for _, iosDevice := range parsedConfig.IOS { @@ -446,21 +444,20 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) { return nil, errors.Wrap(code.InvalidCaseError, fmt.Sprintf("parse ios config failed: %v", err)) } + device, err := uixt.NewIOSDevice(iosDevice.Options()...) if err != nil { return nil, errors.Wrap(err, "init ios device failed") } if err := device.Setup(); err != nil { - return nil, err + return nil, errors.Wrap(err, "setup ios device failed") } - driver, err := device.NewDriver() + driverExt, err := device.NewDriver() if err != nil { - return nil, err + return nil, errors.Wrap(err, "init ios driver failed") } - if err := driver.Setup(); err != nil { - return nil, err - } - r.uixtDrivers[device.UDID] = driver + + r.uixtDrivers[iosDevice.UDID] = driverExt } // parse harmony devices config for _, harmonyDevice := range parsedConfig.Harmony { @@ -469,21 +466,20 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) { return nil, errors.Wrap(code.InvalidCaseError, fmt.Sprintf("parse harmony config failed: %v", err)) } + device, err := uixt.NewHarmonyDevice(harmonyDevice.Options()...) if err != nil { return nil, errors.Wrap(err, "init harmony device failed") } if err := device.Setup(); err != nil { - return nil, err + return nil, errors.Wrap(err, "setup harmony device failed") } - driver, err := device.NewDriver() + driverExt, err := device.NewDriver() if err != nil { - return nil, err + return nil, errors.Wrap(err, "init harmony driver failed") } - if err := driver.Setup(); err != nil { - return nil, err - } - r.uixtDrivers[device.ConnectKey] = driver + + r.uixtDrivers[harmonyDevice.ConnectKey] = driverExt } return parsedConfig, nil @@ -525,7 +521,7 @@ func (r *CaseRunner) parseDeviceConfig(device interface{}, configVariables map[s return nil } -func (r *CaseRunner) GetUIXTDriver(serial string) (driver *uixt.DriverExt, err error) { +func (r *CaseRunner) GetUIXTDriver(serial string) (driver uixt.IDriverExt, err error) { for key, driver := range r.uixtDrivers { // return the driver with the same serial if key == serial { @@ -604,8 +600,8 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa "uuid": uuid, } - if client.Device.LogEnabled() { - log, err1 := client.Driver.StopCaptureLog() + if client.GetDriver().GetDevice().LogEnabled() { + log, err1 := client.GetDriver().StopCaptureLog() if err1 != nil { if err == nil { err = errors.Wrap(err1, "stop capture log failed") diff --git a/server/context.go b/server/context.go index 26697dfc..1be2df86 100644 --- a/server/context.go +++ b/server/context.go @@ -13,7 +13,7 @@ import ( "github.com/httprunner/httprunner/v5/pkg/uixt/option" ) -var uiClients = make(map[string]*uixt.DriverExt) // UI automation clients for iOS and Android, key is udid/serial +var uiClients = make(map[string]uixt.IDriverExt) // UI automation clients for iOS and Android, key is udid/serial func handleDeviceContext() gin.HandlerFunc { return func(c *gin.Context) { @@ -54,9 +54,7 @@ func handleDeviceContext() gin.HandlerFunc { } device.Setup() - driver, err := device.NewDriver( - option.WithDriverImageService(true), - option.WithDriverResultFolder(true)) + driver, err := device.NewDriver() if err != nil { log.Error().Err(err).Str("platform", platform).Str("serial", serial). Msg("failed to init driver") diff --git a/server/model.go b/server/model.go index eed2240f..b27c5b6b 100644 --- a/server/model.go +++ b/server/model.go @@ -1,6 +1,8 @@ package server -import "github.com/httprunner/httprunner/v5/pkg/uixt/option" +import ( + "github.com/httprunner/httprunner/v5/pkg/uixt/option" +) type HttpResponse struct { Code int `json:"code"` @@ -31,7 +33,7 @@ type InputRequest struct { } type ScreenRequest struct { - Options *option.ActionOptions `json:"options,omitempty"` + Options *option.ScreenOptions `json:"options,omitempty"` } type KeycodeRequest struct { diff --git a/step_mobile_ui.go b/step_mobile_ui.go index 34fbe3ff..f15244ea 100644 --- a/step_mobile_ui.go +++ b/step_mobile_ui.go @@ -684,7 +684,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err }, StartTime: startTime.Unix(), } - if app, err1 := uiDriver.Driver.GetForegroundApp(); err1 == nil { + if app, err1 := uiDriver.GetDriver().GetForegroundApp(); err1 == nil { attachments["foreground_app"] = app.AppBaseInfo } else { log.Warn().Err(err1).Msg("save foreground app failed, ignore") @@ -711,7 +711,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err } // save attachments - session := uiDriver.Driver.GetSession() + session := uiDriver.GetDriver().GetSession() for key, value := range session.GetData(true) { attachments[key] = value } @@ -780,7 +780,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err return stepResult, nil } -func validateUI(ud *uixt.DriverExt, iValidators []interface{}) (validateResults []*ValidationResult, err error) { +func validateUI(ud uixt.IDriverExt, iValidators []interface{}) (validateResults []*ValidationResult, err error) { for _, iValidator := range iValidators { validator, ok := iValidator.(Validator) if !ok {