fix: errors

This commit is contained in:
lilong.129
2025-02-09 10:51:03 +08:00
parent 5e45eb7836
commit 3038fb7430
47 changed files with 710 additions and 910 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ var installCmd = &cobra.Command{
return err return err
} }
err = driverExt.Install(args[0], err = driverExt.GetDriver().GetDevice().Install(args[0],
option.WithReinstall(replace), option.WithReinstall(replace),
option.WithDowngrade(downgrade), option.WithDowngrade(downgrade),
option.WithGrantPermission(grant), option.WithGrantPermission(grant),
+8 -8
View File
@@ -28,24 +28,24 @@ func init() {
fmt.Printf("=== start running cases, serial=%s, runTimes=%d ===\n", serial, runTimes) 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)) device, _ := uixt.NewAndroidDevice(option.WithSerialNumber(serial))
driver, err = device.NewDriver() driver, err = device.NewDriver()
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = driver.Driver.AppTerminate(pkgName) _, err = driver.GetDriver().AppTerminate(pkgName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = driver.Driver.Homescreen() err = driver.GetDriver().Homescreen()
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = driver.Driver.AppLaunch(pkgName) err = driver.GetDriver().AppLaunch(pkgName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -67,7 +67,7 @@ func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) {
return driver, nil return driver, nil
} }
func watchVideo(driver *uixt.DriverExt) (err error) { func watchVideo(driver uixt.IDriverExt) (err error) {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2) err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2)
if err != nil { if err != nil {
@@ -96,7 +96,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
if err != nil { if err != nil {
// 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed) // 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed)
// 退出回到推荐页 // 退出回到推荐页
driver.Driver.PressBack() driver.GetDriver().PressBack()
return nil return nil
} }
@@ -104,7 +104,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
// 返回视频页面 // 返回视频页面
err = driver.Driver.PressBack() err = driver.GetDriver().PressBack()
if err != nil { if err != nil {
return err return err
} }
@@ -112,7 +112,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
// 返回推荐页 // 返回推荐页
err = driver.Driver.PressBack() err = driver.GetDriver().PressBack()
if err != nil { if err != nil {
return err return err
} }
+5 -5
View File
@@ -28,7 +28,7 @@ func init() {
fmt.Printf("=== start running cases, serial=%s, runTimes=%d ===\n", serial, runTimes) 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)) device, _ := uixt.NewIOSDevice(option.WithUDID(serial))
driver, err = device.NewDriver() driver, err = device.NewDriver()
if err != nil { if err != nil {
@@ -67,7 +67,7 @@ func launchAppDriver(pkgName string) (driver *uixt.DriverExt, err error) {
return driver, nil return driver, nil
} }
func watchVideo(driver *uixt.DriverExt) (err error) { func watchVideo(driver uixt.IDriverExt) (err error) {
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2) err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2)
if err != nil { if err != nil {
@@ -96,7 +96,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
if err != nil { if err != nil {
// 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed) // 未找到横屏图标,该页面可能不是横版视频(直播|广告|Feed)
// 退出回到推荐页 // 退出回到推荐页
driver.Driver.PressBack() driver.GetDriver().PressBack()
return nil return nil
} }
@@ -104,7 +104,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
// 返回视频页面 // 返回视频页面
err = driver.Driver.PressBack() err = driver.GetDriver().PressBack()
if err != nil { if err != nil {
return err return err
} }
@@ -112,7 +112,7 @@ func watchVideo(driver *uixt.DriverExt) (err error) {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
// 返回推荐页 // 返回推荐页
err = driver.Driver.PressBack() err = driver.GetDriver().PressBack()
if err != nil { if err != nil {
return err return err
} }
+21 -4
View File
@@ -8,6 +8,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/httprunner/httprunner/v5/pkg/uixt" "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{ var rootCmd = &cobra.Command{
@@ -15,21 +17,36 @@ var rootCmd = &cobra.Command{
Short: "Monitor FIFA World Cup Live", Short: "Monitor FIFA World Cup Live",
Version: "2022.12.03.0018", Version: "2022.12.03.0018",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
var device uixt.IDevice var driver uixt.IDriver
var bundleID string var bundleID string
if iosApp != "" { if iosApp != "" {
log.Info().Str("bundleID", iosApp).Msg("init ios device") 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 bundleID = iosApp
} else if androidApp != "" { } else if androidApp != "" {
log.Info().Str("bundleID", androidApp).Msg("init android device") 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 bundleID = androidApp
} else { } else {
return errors.New("android or ios app bundldID is required") 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 { if auto {
wc.EnterLive(bundleID) wc.EnterLive(bundleID)
+13 -18
View File
@@ -37,7 +37,7 @@ func convertTimeToSeconds(timeStr string) (int, error) {
return seconds, nil return seconds, nil
} }
func initIOSDevice(uuid string) uixt.IDevice { func initIOSDriver(uuid string) uixt.IDriverExt {
device, err := uixt.NewIOSDevice( device, err := uixt.NewIOSDevice(
option.WithUDID(uuid), option.WithUDID(uuid),
option.WithWDAPort(8700), option.WithWDAMjpegPort(8800), option.WithWDAPort(8700), option.WithWDAMjpegPort(8800),
@@ -47,15 +47,17 @@ func initIOSDevice(uuid string) uixt.IDevice {
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("failed to init ios device") 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)) device, err := uixt.NewAndroidDevice(option.WithSerialNumber(uuid))
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("failed to init android device") log.Fatal().Err(err).Msg("failed to init android device")
} }
return device driver, _ := device.NewDriver()
return driver
} }
type timeLog struct { type timeLog struct {
@@ -66,10 +68,9 @@ type timeLog struct {
} }
type WorldCupLive struct { type WorldCupLive struct {
driver *uixt.DriverExt driver uixt.IDriverExt
file *os.File file *os.File
resultDir string resultDir string
UUID string `json:"uuid"`
MatchName string `json:"matchName"` MatchName string `json:"matchName"`
BundleID string `json:"bundleID"` BundleID string `json:"bundleID"`
StartTime string `json:"startTime"` StartTime string `json:"startTime"`
@@ -80,12 +81,7 @@ type WorldCupLive struct {
PerfFile string `json:"perf"` PerfFile string `json:"perf"`
} }
func NewWorldCupLive(device uixt.IDevice, matchName, bundleID string, duration, interval int) *WorldCupLive { func NewWorldCupLive(driver uixt.IDriverExt, matchName, bundleID string, duration, interval int) *WorldCupLive {
driverExt, err := device.NewDriver()
if err != nil {
log.Fatal().Err(err).Msg("failed to init driver")
}
if matchName == "" { if matchName == "" {
matchName = "unknown-match" 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) matchName = fmt.Sprintf("%s-%s", startTime.Format("2006-01-02"), matchName)
resultDir := filepath.Join("worldcuplive", matchName, startTime.Format("15:04:05")) 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") 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") log.Fatal().Err(err).Msg("failed to open file")
} }
// write title // 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") f.WriteString("utc_time\tutc_timestamp\tlive_time\tlive_seconds\n")
if interval == 0 { if interval == 0 {
@@ -115,10 +111,9 @@ func NewWorldCupLive(device uixt.IDevice, matchName, bundleID string, duration,
} }
return &WorldCupLive{ return &WorldCupLive{
driver: driverExt, driver: driver,
file: f, file: f,
resultDir: resultDir, resultDir: resultDir,
UUID: device.UUID(),
BundleID: bundleID, BundleID: bundleID,
Duration: duration, Duration: duration,
Interval: interval, Interval: interval,
@@ -177,13 +172,13 @@ func (wc *WorldCupLive) EnterLive(bundleID string) error {
log.Info().Msg("enter world cup live") log.Info().Msg("enter world cup live")
// kill app // kill app
_, err := wc.driver.Driver.AppTerminate(bundleID) _, err := wc.driver.GetDriver().AppTerminate(bundleID)
if err != nil { if err != nil {
log.Error().Err(err).Msg("terminate app failed") log.Error().Err(err).Msg("terminate app failed")
} }
// launch app // launch app
err = wc.driver.Driver.AppLaunch(bundleID) err = wc.driver.GetDriver().AppLaunch(bundleID)
if err != nil { if err != nil {
log.Error().Err(err).Msg("launch app failed") log.Error().Err(err).Msg("launch app failed")
return err return err
+4 -4
View File
@@ -33,17 +33,17 @@ func TestConvertTimeToSeconds(t *testing.T) {
func TestMainIOS(t *testing.T) { func TestMainIOS(t *testing.T) {
uuid := "00008030-00194DA421C1802E" uuid := "00008030-00194DA421C1802E"
device := initIOSDevice(uuid) driver := initIOSDriver(uuid)
bundleID := "com.ss.iphone.ugc.Aweme" bundleID := "com.ss.iphone.ugc.Aweme"
wc := NewWorldCupLive(device, "", bundleID, 30, 10) wc := NewWorldCupLive(driver, "", bundleID, 30, 10)
wc.EnterLive(bundleID) wc.EnterLive(bundleID)
wc.Start() wc.Start()
} }
func TestMainAndroid(t *testing.T) { func TestMainAndroid(t *testing.T) {
device := initAndroidDevice(uuid) driver := initAndroidDriver(uuid)
bundleID := "com.ss.android.ugc.aweme" bundleID := "com.ss.android.ugc.aweme"
wc := NewWorldCupLive(device, "", bundleID, 30, 10) wc := NewWorldCupLive(driver, "", bundleID, 30, 10)
wc.EnterLive(bundleID) wc.EnterLive(bundleID)
wc.Start() wc.Start()
} }
+1 -1
View File
@@ -1 +1 @@
v5.0.0+2502081446 v5.0.0+2502091051
View File
+16 -23
View File
@@ -9,13 +9,15 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "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" "github.com/pkg/errors"
) )
type ICVService interface { type ICVService interface {
// returns CV result including ocr texts, uploaded image url, etc // returns CV result including ocr texts, uploaded image url, etc
ReadFromBuffer(imageBuf *bytes.Buffer, opts ...ScreenShotOption) (*CVResult, error) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...option.ActionOption) (*CVResult, error)
ReadFromPath(imagePath string, opts ...ScreenShotOption) (*CVResult, error) ReadFromPath(imagePath string, opts ...option.ActionOption) (*CVResult, error)
} }
type CVResult struct { type CVResult struct {
@@ -75,8 +77,8 @@ type OCRText struct {
Rect image.Rectangle `json:"-"` Rect image.Rectangle `json:"-"`
} }
func (t OCRText) Size() Size { func (t OCRText) Size() types.Size {
return Size{ return types.Size{
Width: t.Rect.Dx(), Width: t.Rect.Dx(),
Height: t.Rect.Dy(), Height: t.Rect.Dy(),
} }
@@ -105,7 +107,7 @@ func (t OCRTexts) texts() (texts []string) {
return texts return texts
} }
func (t OCRTexts) FilterScope(scope AbsScope) (results OCRTexts) { func (t OCRTexts) FilterScope(scope option.AbsScope) (results OCRTexts) {
for _, ocrText := range t { for _, ocrText := range t {
rect := ocrText.Rect rect := ocrText.Rect
@@ -127,8 +129,8 @@ func (t OCRTexts) FilterScope(scope AbsScope) (results OCRTexts) {
// FindText returns matched text with options // FindText returns matched text with options
// Notice: filter scope should be specified with WithAbsScope // Notice: filter scope should be specified with WithAbsScope
func (t OCRTexts) FindText(text string, opts ...ScreenFilterOption) (result OCRText, err error) { func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCRText, err error) {
options := NewScreenFilterOptions(opts...) options := option.NewActionOptions(opts...)
var results []OCRText var results []OCRText
for _, ocrText := range t.FilterScope(options.AbsScope) { 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 return results[idx], nil
} }
func (t OCRTexts) FindTexts(texts []string, opts ...ScreenFilterOption) (results OCRTexts, err error) { func (t OCRTexts) FindTexts(texts []string, opts ...option.ActionOption) (results OCRTexts, err error) {
options := NewScreenFilterOptions(opts...) options := option.NewActionOptions(opts...)
for _, text := range texts { for _, text := range texts {
ocrText, err := t.FindText(text, opts...) ocrText, err := t.FindText(text, opts...)
if err != nil { if err != nil {
@@ -239,7 +241,7 @@ func (box Box) Center() PointF {
type UIResults []UIResult type UIResults []UIResult
func (u UIResults) FilterScope(scope AbsScope) (results UIResults) { func (u UIResults) FilterScope(scope option.AbsScope) (results UIResults) {
for _, uiResult := range u { for _, uiResult := range u {
rect := image.Rectangle{ rect := image.Rectangle{
Min: image.Point{ Min: image.Point{
@@ -267,8 +269,8 @@ func (u UIResults) FilterScope(scope AbsScope) (results UIResults) {
return return
} }
func (u UIResults) GetUIResult(opts ...ScreenFilterOption) (UIResult, error) { func (u UIResults) GetUIResult(opts ...option.ActionOption) (UIResult, error) {
options := NewScreenFilterOptions(opts...) options := option.NewActionOptions(opts...)
uiResults := u.FilterScope(options.AbsScope) uiResults := u.FilterScope(options.AbsScope)
if len(uiResults) == 0 { if len(uiResults) == 0 {
return UIResult{}, errors.Wrap(code.CVResultNotFoundError, 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 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 { type Screen struct {
StatusBarSize Size `json:"statusBarSize"` StatusBarSize types.Size `json:"statusBarSize"`
Scale float64 `json:"scale"` Scale float64 `json:"scale"`
} }
@@ -16,6 +16,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
var client = &http.Client{ var client = &http.Client{
@@ -46,7 +47,7 @@ func NewVEDEMImageService() (*vedemCVService, error) {
// ui - get ui position by type(s) // ui - get ui position by type(s)
type vedemCVService struct{} type vedemCVService struct{}
func (s *vedemCVService) ReadFromPath(imagePath string, opts ...ScreenShotOption) ( func (s *vedemCVService) ReadFromPath(imagePath string, opts ...option.ActionOption) (
imageResult *CVResult, err error) { imageResult *CVResult, err error) {
imageBuf, err := os.ReadFile(imagePath) imageBuf, err := os.ReadFile(imagePath)
if err != nil { if err != nil {
@@ -58,9 +59,9 @@ func (s *vedemCVService) ReadFromPath(imagePath string, opts ...ScreenShotOption
return return
} }
func (s *vedemCVService) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...ScreenShotOption) ( func (s *vedemCVService) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...option.ActionOption) (
imageResult *CVResult, err error) { imageResult *CVResult, err error) {
actionOptions := NewScreenShotOptions(opts...) actionOptions := option.NewActionOptions(opts...)
screenshotActions := actionOptions.List() screenshotActions := actionOptions.List()
if len(screenshotActions) == 0 { if len(screenshotActions) == 0 {
// skip // skip
+21 -16
View File
@@ -24,7 +24,9 @@ import (
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/gadb" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
const ( const (
@@ -147,7 +149,7 @@ func (dev *AndroidDevice) LogEnabled() bool {
return dev.LogOn 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 var driver IDriver
if dev.UIA2 || dev.LogOn { if dev.UIA2 || dev.LogOn {
driver, err = NewUIA2Driver(dev) 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") 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 { if dev.LogOn {
err = driverExt.Driver.StartCaptureLog("hrp_adb_log") err = driver.StartCaptureLog("hrp_adb_log")
if err != nil { if err != nil {
return nil, err 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 return driverExt, nil
} }
@@ -301,17 +306,17 @@ func (dev *AndroidDevice) Uninstall(packageName string) error {
return err 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' // adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'
output, err := dev.RunShellCommand("dumpsys", "window", "|", "grep", "-E", "'mCurrentFocus|mFocusedApp'") output, err := dev.RunShellCommand("dumpsys", "window", "|", "grep", "-E", "'mCurrentFocus|mFocusedApp'")
if err != nil { 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} // mCurrentFocus=Window{a33bc55 u0 com.miui.home/com.miui.home.launcher.Launcher}
reFocus := regexp.MustCompile(`mCurrentFocus=Window{.*? (\S+)/(\S+)}`) reFocus := regexp.MustCompile(`mCurrentFocus=Window{.*? (\S+)/(\S+)}`)
matches := reFocus.FindStringSubmatch(output) matches := reFocus.FindStringSubmatch(output)
if len(matches) == 3 { if len(matches) == 3 {
windowInfo = WindowInfo{ windowInfo = types.WindowInfo{
PackageName: matches[1], PackageName: matches[1],
Activity: matches[2], Activity: matches[2],
} }
@@ -321,7 +326,7 @@ func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error)
reApp := regexp.MustCompile(`mFocusedApp=ActivityRecord{.*? (\S+)/(\S+?)\s`) reApp := regexp.MustCompile(`mFocusedApp=ActivityRecord{.*? (\S+)/(\S+?)\s`)
matches = reApp.FindStringSubmatch(output) matches = reApp.FindStringSubmatch(output)
if len(matches) == 3 { if len(matches) == 3 {
windowInfo = WindowInfo{ windowInfo = types.WindowInfo{
PackageName: matches[1], PackageName: matches[1],
Activity: matches[2], Activity: matches[2],
} }
@@ -331,24 +336,24 @@ func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error)
// adb shell dumpsys activity activities | grep mResumedActivity // adb shell dumpsys activity activities | grep mResumedActivity
output, err = dev.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "mResumedActivity") output, err = dev.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "mResumedActivity")
if err != nil { 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} // mResumedActivity: ActivityRecord{2db504f u0 com.miui.home/.launcher.Launcher t2}
reActivity := regexp.MustCompile(`mResumedActivity: ActivityRecord{.*? (\S+)/(\S+?)\s`) reActivity := regexp.MustCompile(`mResumedActivity: ActivityRecord{.*? (\S+)/(\S+?)\s`)
matches = reActivity.FindStringSubmatch(output) matches = reActivity.FindStringSubmatch(output)
if len(matches) == 3 { if len(matches) == 3 {
windowInfo = WindowInfo{ windowInfo = types.WindowInfo{
PackageName: matches[1], PackageName: matches[1],
Activity: matches[2], Activity: matches[2],
} }
return windowInfo, nil 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) { func (dev *AndroidDevice) GetPackageInfo(packageName string) (types.AppInfo, error) {
appInfo := AppInfo{ appInfo := types.AppInfo{
Name: packageName, Name: packageName,
} }
// get package version // get package version
+20 -19
View File
@@ -23,8 +23,9 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/utf7" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) { func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) {
@@ -86,7 +87,7 @@ func (ad *ADBDriver) DeleteSession() (err error) {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (ad *ADBDriver) Status() (deviceStatus DeviceStatus, err error) { func (ad *ADBDriver) Status() (deviceStatus types.DeviceStatus, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
@@ -95,22 +96,22 @@ func (ad *ADBDriver) GetDevice() IDevice {
return ad.AndroidDevice return ad.AndroidDevice
} }
func (ad *ADBDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) { func (ad *ADBDriver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
func (ad *ADBDriver) Location() (location Location, err error) { func (ad *ADBDriver) Location() (location types.Location, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
func (ad *ADBDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) { func (ad *ADBDriver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
func (ad *ADBDriver) getWindowSize() (size ai.Size, err error) { func (ad *ADBDriver) getWindowSize() (size types.Size, err error) {
// adb shell wm size // adb shell wm size
output, err := ad.runShellCommand("wm", "size") output, err := ad.runShellCommand("wm", "size")
if err != nil { if err != nil {
@@ -136,14 +137,14 @@ func (ad *ADBDriver) getWindowSize() (size ai.Size, err error) {
ss := strings.Split(resolution, "x") ss := strings.Split(resolution, "x")
width, _ := strconv.Atoi(ss[0]) width, _ := strconv.Atoi(ss[0])
height, _ := strconv.Atoi(ss[1]) 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") err = errors.New("physical window size not found by adb")
return return
} }
func (ad *ADBDriver) WindowSize() (size ai.Size, err error) { func (ad *ADBDriver) WindowSize() (size types.Size, err error) {
if !ad.windowSize.IsNil() { if !ad.windowSize.IsNil() {
// use cached window size // use cached window size
return ad.windowSize, nil return ad.windowSize, nil
@@ -157,11 +158,11 @@ func (ad *ADBDriver) WindowSize() (size ai.Size, err error) {
orientation, err2 := ad.Orientation() orientation, err2 := ad.Orientation()
if err2 != nil { if err2 != nil {
// Notice: do not return err if get window orientation failed // Notice: do not return err if get window orientation failed
orientation = OrientationPortrait orientation = types.OrientationPortrait
log.Warn().Err(err2).Msgf( log.Warn().Err(err2).Msgf(
"get window orientation failed, use default %s", orientation) "get window orientation failed, use default %s", orientation)
} }
if orientation != OrientationPortrait { if orientation != types.OrientationPortrait {
size.Width, size.Height = size.Height, size.Width size.Width, size.Height = size.Height, size.Width
} }
@@ -243,7 +244,7 @@ func (ad *ADBDriver) StopCamera() (err error) {
return 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'") output, err := ad.runShellCommand("dumpsys", "input", "|", "grep", "'SurfaceOrientation'")
if err != nil { if err != nil {
return return
@@ -252,9 +253,9 @@ func (ad *ADBDriver) Orientation() (orientation Orientation, err error) {
matches := re.FindStringSubmatch(output) matches := re.FindStringSubmatch(output)
if len(matches) > 1 { // 确保找到了匹配项 if len(matches) > 1 { // 确保找到了匹配项
if matches[1] == "0" || matches[1] == "2" { if matches[1] == "0" || matches[1] == "2" {
return OrientationPortrait, nil return types.OrientationPortrait, nil
} else if matches[1] == "1" || matches[1] == "3" { } else if matches[1] == "1" || matches[1] == "3" {
return OrientationLandscapeLeft, nil return types.OrientationLandscapeLeft, nil
} }
} }
err = fmt.Errorf("not found SurfaceOrientation value") err = fmt.Errorf("not found SurfaceOrientation value")
@@ -490,12 +491,12 @@ func (ad *ADBDriver) ForceTouchFloat(x, y, pressure float64, second ...float64)
return return
} }
func (ad *ADBDriver) SetPasteboard(contentType PasteboardType, content string) (err error) { func (ad *ADBDriver) SetPasteboard(contentType types.PasteboardType, content string) (err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return 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 err = errDriverNotImplemented
return return
} }
@@ -618,17 +619,17 @@ func (ad *ADBDriver) Clear(packageName string) error {
return nil return nil
} }
func (ad *ADBDriver) PressButton(devBtn DeviceButton) (err error) { func (ad *ADBDriver) PressButton(devBtn types.DeviceButton) (err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
func (ad *ADBDriver) Rotation() (rotation Rotation, err error) { func (ad *ADBDriver) Rotation() (rotation types.Rotation, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
func (ad *ADBDriver) SetRotation(rotation Rotation) (err error) { func (ad *ADBDriver) SetRotation(rotation types.Rotation) (err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
@@ -856,7 +857,7 @@ func (ad *ADBDriver) GetDriverResults() []*DriverRequests {
return nil return nil
} }
func (ad *ADBDriver) GetForegroundApp() (app AppInfo, err error) { func (ad *ADBDriver) GetForegroundApp() (app types.AppInfo, err error) {
packageInfo, err := ad.runShellCommand( packageInfo, err := ad.runShellCommand(
"CLASSPATH=/data/local/tmp/evalite", "app_process", "/", "CLASSPATH=/data/local/tmp/evalite", "app_process", "/",
"com.bytedance.iesqa.eval_process.PackageService", "2>/dev/null") "com.bytedance.iesqa.eval_process.PackageService", "2>/dev/null")
+5 -4
View File
@@ -15,6 +15,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
const ( const (
@@ -166,17 +167,17 @@ func (sad *StubAndroidDriver) close() error {
return nil return nil
} }
func (sad *StubAndroidDriver) Status() (DeviceStatus, error) { func (sad *StubAndroidDriver) Status() (types.DeviceStatus, error) {
app, err := sad.GetForegroundApp() app, err := sad.GetForegroundApp()
if err != nil { if err != nil {
return DeviceStatus{}, err return types.DeviceStatus{}, err
} }
res, err := sad.sendCommand(app.PackageName, "Hello", nil) res, err := sad.sendCommand(app.PackageName, "Hello", nil)
if err != nil { if err != nil {
return DeviceStatus{}, err return types.DeviceStatus{}, err
} }
log.Info().Msg(fmt.Sprintf("ping stub result :%v", res)) 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) { func (sad *StubAndroidDriver) Source(srcOpt ...option.SourceOption) (source string, err error) {
+28 -28
View File
@@ -16,8 +16,8 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/utf7" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
var errDriverNotImplemented = errors.New("driver method not implemented") var errDriverNotImplemented = errors.New("driver method not implemented")
@@ -133,12 +133,12 @@ func (ud *UIA2Driver) DeleteSession() (err error) {
return err 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")) // register(getHandler, new Status("/wd/hub/status"))
var rawResp rawResponse var rawResp rawResponse
// Notice: use Driver.GET instead of httpGET to avoid loop calling // Notice: use Driver.GET instead of httpGET to avoid loop calling
if rawResp, err = ud.GET("/status"); err != nil { if rawResp, err = ud.GET("/status"); err != nil {
return DeviceStatus{Ready: false}, err return types.DeviceStatus{Ready: false}, err
} }
reply := new(struct { reply := new(struct {
Value struct { Value struct {
@@ -147,34 +147,34 @@ func (ud *UIA2Driver) Status() (deviceStatus DeviceStatus, err error) {
} }
}) })
if err = json.Unmarshal(rawResp, reply); err != nil { 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")) // register(getHandler, new GetDeviceInfo("/wd/hub/session/:sessionId/appium/device/info"))
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "appium/device/info"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return DeviceInfo{}, err return types.DeviceInfo{}, err
} }
deviceInfo = reply.Value.DeviceInfo deviceInfo = reply.Value.DeviceInfo
return 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")) // register(getHandler, new GetBatteryInfo("/wd/hub/session/:sessionId/appium/device/battery_info"))
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "appium/device/battery_info"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return BatteryInfo{}, err return types.BatteryInfo{}, err
} }
if reply.Value.Level == -1 || reply.Value.Status == -1 { if reply.Value.Level == -1 || reply.Value.Status == -1 {
return reply.Value.BatteryInfo, errors.New("cannot be retrieved from the system") 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 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")) // register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size"))
if !ud.windowSize.IsNil() { if !ud.windowSize.IsNil() {
// use cached window size // use cached window size
@@ -192,11 +192,11 @@ func (ud *UIA2Driver) WindowSize() (size ai.Size, err error) {
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "window/:windowHandle/size"); err != nil { 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 { 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 size = reply.Value.Size
@@ -204,9 +204,9 @@ func (ud *UIA2Driver) WindowSize() (size ai.Size, err error) {
orientation, err := ud.Orientation() orientation, err := ud.Orientation()
if err != nil { if err != nil {
log.Warn().Err(err).Msgf("window size get orientation failed, use default orientation") 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 size.Width, size.Height = size.Height, size.Width
} }
@@ -244,13 +244,13 @@ func (ud *UIA2Driver) PressKeyCodes(keyCode KeyCode, metaState KeyMeta, flags ..
return 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:)] // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "/orientation"); err != nil { if rawResp, err = ud.httpGET("/session", ud.sessionID, "/orientation"); err != nil {
return "", err return "", err
} }
reply := new(struct{ Value Orientation }) reply := new(struct{ Value types.Orientation })
if err = json.Unmarshal(rawResp, reply); err != nil { if err = json.Unmarshal(rawResp, reply); err != nil {
return "", err return "", err
} }
@@ -418,7 +418,7 @@ func (ud *UIA2Driver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Actio
return err 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 lbl := content
const defaultLabelLen = 10 const defaultLabelLen = 10
@@ -436,9 +436,9 @@ func (ud *UIA2Driver) SetPasteboard(contentType PasteboardType, content string)
return 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 { if len(contentType) == 0 {
contentType = PasteboardTypePlaintext contentType = types.PasteboardTypePlaintext
} }
// register(postHandler, new GetClipboard("/wd/hub/session/:sessionId/appium/device/get_clipboard")) // register(postHandler, new GetClipboard("/wd/hub/session/:sessionId/appium/device/get_clipboard"))
data := map[string]interface{}{ data := map[string]interface{}{
@@ -541,15 +541,15 @@ func (ud *UIA2Driver) Input(text string, opts ...option.ActionOption) (err error
return ud.SendKeys(text, opts...) 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")) // register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation"))
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "rotation"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return Rotation{}, err return types.Rotation{}, err
} }
rotation = reply.Value rotation = reply.Value
+26 -25
View File
@@ -10,9 +10,10 @@ import (
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "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) { func setupAndroidAdbDriver(t *testing.T) {
device, err := NewAndroidDevice() device, err := NewAndroidDevice()
@@ -91,7 +92,7 @@ func TestDriver_DeviceSize(t *testing.T) {
func TestDriver_Source(t *testing.T) { func TestDriver_Source(t *testing.T) {
setupAndroidUIA2Driver(t) setupAndroidUIA2Driver(t)
source, err := driverExt.Driver.Source() source, err := driverExt.GetDriver().Source()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -140,7 +141,7 @@ func TestDriver_DeviceInfo(t *testing.T) {
func TestDriver_Tap(t *testing.T) { func TestDriver_Tap(t *testing.T) {
setupAndroidUIA2Driver(t) setupAndroidUIA2Driver(t)
driverExt.Driver.StartCaptureLog("") driverExt.GetDriver().StartCaptureLog("")
err := driverExt.TapXY(0.5, 0.5, err := driverExt.TapXY(0.5, 0.5,
option.WithIdentifier("test"), option.WithIdentifier("test"),
option.WithPressDuration(4)) option.WithPressDuration(4))
@@ -149,18 +150,18 @@ func TestDriver_Tap(t *testing.T) {
} }
//time.Sleep(time.Second) //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 { //if err != nil {
// t.Fatal(err) // t.Fatal(err)
//} //}
//time.Sleep(time.Second) //time.Sleep(time.Second)
//result, _ := driverExt.Driver.StopCaptureLog() //result, _ := driverExt.GetDriver().StopCaptureLog()
//t.Log(result) //t.Log(result)
} }
func TestDriver_Swipe(t *testing.T) { func TestDriver_Swipe(t *testing.T) {
setupAndroidUIA2Driver(t) setupAndroidUIA2Driver(t)
err := driverExt.Driver.Swipe(400, 1000, 400, 500, err := driverExt.GetDriver().Swipe(400, 1000, 400, 500,
option.WithPressDuration(0.5)) option.WithPressDuration(0.5))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -192,7 +193,7 @@ func TestDriver_Drag(t *testing.T) {
func TestDriver_SendKeys(t *testing.T) { func TestDriver_SendKeys(t *testing.T) {
setupAndroidUIA2Driver(t) setupAndroidUIA2Driver(t)
err := driverExt.Driver.SendKeys("辽宁省沈阳市新民市民族街36-4", err := driverExt.GetDriver().SendKeys("辽宁省沈阳市新民市民族街36-4",
option.WithIdentifier("test")) option.WithIdentifier("test"))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -222,7 +223,7 @@ func TestDriver_PressBack(t *testing.T) {
func TestDriver_SetRotation(t *testing.T) { func TestDriver_SetRotation(t *testing.T) {
// err = driver.SetRotation(Rotation{Z: 0}) // err = driver.SetRotation(Rotation{Z: 0})
err := driver.SetRotation(Rotation{Z: 270}) err := driver.SetRotation(types.Rotation{Z: 270})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -230,10 +231,10 @@ func TestDriver_SetRotation(t *testing.T) {
func TestDriver_GetOrientation(t *testing.T) { func TestDriver_GetOrientation(t *testing.T) {
setupAndroidUIA2Driver(t) setupAndroidUIA2Driver(t)
_, _ = driverExt.Driver.AppTerminate("com.quark.browser") _, _ = driverExt.GetDriver().AppTerminate("com.quark.browser")
_ = driverExt.Driver.AppLaunch("com.quark.browser") _ = driverExt.GetDriver().AppLaunch("com.quark.browser")
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
_ = driverExt.Driver.Homescreen() _ = driverExt.GetDriver().Homescreen()
} }
func Test_getFreePort(t *testing.T) { func Test_getFreePort(t *testing.T) {
@@ -251,12 +252,12 @@ func TestDriver_AppLaunch(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = driver.Driver.AppLaunch("com.android.settings") err = driver.GetDriver().AppLaunch("com.android.settings")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
raw, err := driver.Driver.Screenshot() raw, err := driver.GetDriver().Screenshot()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -268,10 +269,10 @@ func TestDriver_IsAppInForeground(t *testing.T) {
setupAndroidUIA2Driver(t) setupAndroidUIA2Driver(t)
// setupAndroidAdbDriver(t) // setupAndroidAdbDriver(t)
err := driverExt.Driver.AppLaunch("com.android.settings") err := driverExt.GetDriver().AppLaunch("com.android.settings")
checkErr(t, err) checkErr(t, err)
app, err := driverExt.Driver.GetForegroundApp() app, err := driverExt.GetDriver().GetForegroundApp()
checkErr(t, err) checkErr(t, err)
if app.PackageName != "com.android.settings" { if app.PackageName != "com.android.settings" {
t.FailNow() t.FailNow()
@@ -280,18 +281,18 @@ func TestDriver_IsAppInForeground(t *testing.T) {
t.FailNow() t.FailNow()
} }
err = driverExt.Driver.AssertForegroundApp("com.android.settings") err = driverExt.GetDriver().AssertForegroundApp("com.android.settings")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
_, err = driverExt.Driver.AppTerminate("com.android.settings") _, err = driverExt.GetDriver().AppTerminate("com.android.settings")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
err = driverExt.Driver.AssertForegroundApp("com.android.settings") err = driverExt.GetDriver().AssertForegroundApp("com.android.settings")
if err == nil { if err == nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -304,19 +305,19 @@ func TestDriver_KeepAlive(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = driver.Driver.AppLaunch("com.android.settings") err = driver.GetDriver().AppLaunch("com.android.settings")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = driver.Driver.Screenshot() _, err = driver.GetDriver().Screenshot()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(60 * time.Second) time.Sleep(60 * time.Second)
_, err = driver.Driver.Screenshot() _, err = driver.GetDriver().Screenshot()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -329,7 +330,7 @@ func TestDriver_AppTerminate(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, err = driver.Driver.AppTerminate("tv.danmaku.bili") _, err = driver.GetDriver().AppTerminate("tv.danmaku.bili")
if err != nil { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -399,7 +400,7 @@ func TestTapTexts(t *testing.T) {
func TestRecordVideo(t *testing.T) { func TestRecordVideo(t *testing.T) {
setupAndroidAdbDriver(t) setupAndroidAdbDriver(t)
path, err := driverExt.Driver.(*ADBDriver).RecordScreen("", 5*time.Second) path, err := driverExt.GetDriver().(*ADBDriver).RecordScreen("", 5*time.Second)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -409,7 +410,7 @@ func TestRecordVideo(t *testing.T) {
func Test_Android_Backspace(t *testing.T) { func Test_Android_Backspace(t *testing.T) {
setupAndroidAdbDriver(t) setupAndroidAdbDriver(t)
err := driverExt.Driver.Backspace(1) err := driverExt.GetDriver().Backspace(1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+2 -4
View File
@@ -22,16 +22,14 @@ func TestIOSDemo(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
capabilities := option.NewCapabilities() driverExt, err := device.NewDriver()
capabilities.WithDefaultAlertAction(option.AlertActionAccept) // or uixt.AlertActionDismiss
driverExt, err := device.NewDriver(option.WithDriverCapabilities(capabilities))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
// release session // release session
defer func() { defer func() {
driverExt.Driver.DeleteSession() driverExt.GetDriver().DeleteSession()
}() }()
// 持续监测手机屏幕,直到出现青少年模式弹窗后,点击「我知道了」 // 持续监测手机屏幕,直到出现青少年模式弹窗后,点击「我知道了」
+2 -1
View File
@@ -2,6 +2,7 @@ package uixt
import ( import (
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice // current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
@@ -14,7 +15,7 @@ type IDevice interface {
Install(appPath string, opts ...option.InstallOption) error Install(appPath string, opts ...option.InstallOption) error
Uninstall(packageName string) error Uninstall(packageName string) error
GetPackageInfo(packageName string) (AppInfo, error) GetPackageInfo(packageName string) (types.AppInfo, error)
// TODO: remove? // TODO: remove?
LogEnabled() bool LogEnabled() bool
+36 -34
View File
@@ -16,8 +16,9 @@ import (
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/json" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
var ( var (
@@ -40,10 +41,10 @@ type IDriver interface {
// GetSession returns session cache, including requests, screenshots, etc. // GetSession returns session cache, including requests, screenshots, etc.
GetSession() *Session GetSession() *Session
Status() (DeviceStatus, error) Status() (types.DeviceStatus, error)
GetDevice() IDevice GetDevice() IDevice
DeviceInfo() (DeviceInfo, error) DeviceInfo() (types.DeviceInfo, error)
// Location Returns device location data. // 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' // 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. // since the location service needs some time to update the location data.
Location() (Location, error) Location() (types.Location, error)
BatteryInfo() (BatteryInfo, error) BatteryInfo() (types.BatteryInfo, error)
// WindowSize Return the width and height in portrait mode. // 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, // when getting the window size in wda/ui2/adb, if the device is in landscape mode,
// the width and height will be reversed. // the width and height will be reversed.
WindowSize() (ai.Size, error) WindowSize() (types.Size, error)
Screen() (ai.Screen, error) Screen() (ai.Screen, error)
Scale() (float64, 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 // Either `true` if the app has been successfully terminated or `false` if it was not running
AppTerminate(packageName string) (bool, error) AppTerminate(packageName string) (bool, error)
// GetForegroundApp returns current foreground app package name and activity name // 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 returns nil if the given package and activity are in foreground
AssertForegroundApp(packageName string, activityType ...string) error AssertForegroundApp(packageName string, activityType ...string) error
@@ -87,10 +88,10 @@ type IDriver interface {
// StopCamera Stops the camera for recording // StopCamera Stops the camera for recording
StopCamera() error StopCamera() error
Orientation() (orientation Orientation, err error) Orientation() (orientation types.Orientation, err error)
SetRotation(rotation Rotation) (err error) SetRotation(rotation types.Rotation) (err error)
Rotation() (rotation Rotation, err error) Rotation() (rotation types.Rotation, err error)
// Tap Sends a tap event at the coordinate. // Tap Sends a tap event at the coordinate.
Tap(x, y float64, opts ...option.ActionOption) error 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 Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error
// SetPasteboard Sets data to the general pasteboard // 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. // GetPasteboard Gets the data contained in the general pasteboard.
// It worked when `WDA` was foreground. https://github.com/appium/WebDriverAgent/issues/330 // 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 SetIme(ime string) error
@@ -128,7 +129,7 @@ type IDriver interface {
Clear(packageName string) error Clear(packageName string) error
// PressButton Presses the corresponding hardware button on the device // PressButton Presses the corresponding hardware button on the device
PressButton(devBtn DeviceButton) error PressButton(devBtn types.DeviceButton) error
// PressBack Presses the back button // PressBack Presses the back button
PressBack(opts ...option.ActionOption) error PressBack(opts ...option.ActionOption) error
@@ -177,6 +178,13 @@ type IDriverExt interface {
GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error)
GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, 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 // swipe
SwipeRelative(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error SwipeRelative(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error
@@ -184,15 +192,25 @@ type IDriverExt interface {
SwipeDown(opts ...option.ActionOption) error SwipeDown(opts ...option.ActionOption) error
SwipeLeft(opts ...option.ActionOption) error SwipeLeft(opts ...option.ActionOption) error
SwipeRight(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) { func NewDriverExt(driver IDriver, opts ...ai.AIServiceOption) (IDriverExt, error) {
services := ai.NewAIService(opts...) services := ai.NewAIService(opts...)
driverExt := &DriverExt{ driverExt := &DriverExt{
Driver: driver, Driver: driver,
ImageService: services.ImageService, CVService: services.ICVService,
LLMService: services.ILLMService,
} }
// create results directory // create results directory
// TODO: move to setup
if err := builtin.EnsureFolderExists(config.ResultsPath); err != nil { if err := builtin.EnsureFolderExists(config.ResultsPath); err != nil {
return nil, errors.Wrap(err, "create results directory failed") 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 { type DriverExt struct {
Driver IDriver Driver IDriver
ImageService ai.IImageService // used to extract image data CVService ai.ICVService // OCR/CV
} LLMService ai.ILLMService // LLM
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
} }
func (dExt *DriverExt) GetDriver() IDriver { func (dExt *DriverExt) GetDriver() IDriver {
+5 -4
View File
@@ -13,6 +13,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
type ActionMethod string type ActionMethod string
@@ -169,7 +170,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
ACTION_AppLaunch, action.Params) ACTION_AppLaunch, action.Params)
case ACTION_SwipeToTapApp: case ACTION_SwipeToTapApp:
if appName, ok := action.Params.(string); ok { 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", return fmt.Errorf("invalid %s params, should be app name(string), got %v",
ACTION_SwipeToTapApp, action.Params) 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) return fmt.Errorf("app_terminate params should be bundleId(string), got %v", action.Params)
case ACTION_SetClipboard: case ACTION_SetClipboard:
if text, ok := action.Params.(string); ok { if text, ok := action.Params.(string); ok {
err := dExt.Driver.SetPasteboard(PasteboardTypePlaintext, text) err := dExt.Driver.SetPasteboard(types.PasteboardTypePlaintext, text)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to set clipboard") 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) return fmt.Errorf("invalid %s params: %v", ACTION_DoubleTap, action.Params)
case ACTION_Swipe: case ACTION_Swipe:
params := action.Params params := action.Params
swipeAction := dExt.prepareSwipeAction(params, action.GetOptions()...) swipeAction := prepareSwipeAction(dExt, params, action.GetOptions()...)
return swipeAction(dExt) return swipeAction(dExt)
case ACTION_Input: case ACTION_Input:
// input text on current active element // input text on current active element
@@ -328,7 +329,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
case ACTION_ScreenShot: case ACTION_ScreenShot:
// take screenshot // take screenshot
log.Info().Msg("take screenshot for current screen") log.Info().Msg("take screenshot for current screen")
_, err := dExt.GetScreenResult(action.GetOptions()...) _, err := dExt.GetScreenResult(action.GetScreenOptions()...)
return err return err
case ACTION_StartCamera: case ACTION_StartCamera:
return dExt.Driver.StartCamera() return dExt.Driver.StartCamera()
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code" "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" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
+26 -25
View File
@@ -18,14 +18,15 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
type ScreenResult struct { type ScreenResult struct {
bufSource *bytes.Buffer // raw image buffer bytes bufSource *bytes.Buffer // raw image buffer bytes
ImagePath string `json:"image_path"` // image file path 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 UploadedURL string `json:"uploaded_url"` // uploaded image url
Texts ai.OCRTexts `json:"texts"` // dumped raw OCRTexts Texts ai.OCRTexts `json:"texts"` // dumped raw OCRTexts
Icons ai.UIResultMap `json:"icons"` // CV 识别的图标 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 // GetScreenResult takes a screenshot, returns the image recognition result
func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) { func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) {
actionOptions := option.NewActionOptions(opts...) screenshotOptions := option.NewActionOptions(opts...)
var fileName string var fileName string
screenshotActions := actionOptions.ScreenshotActions() optionsList := screenshotOptions.List()
if actionOptions.ScreenShotFileName != "" { if screenshotOptions.ScreenShotFileName != "" {
fileName = builtin.GenNameWithTimestamp("%d_" + actionOptions.ScreenShotFileName) fileName = builtin.GenNameWithTimestamp("%d_" + screenshotOptions.ScreenShotFileName)
} else if len(screenshotActions) != 0 { } else if len(optionsList) != 0 {
fileName = builtin.GenNameWithTimestamp("%d_" + strings.Join(screenshotActions, "_")) fileName = builtin.GenNameWithTimestamp("%d_" + strings.Join(optionsList, "_"))
} else { } else {
fileName = builtin.GenNameWithTimestamp("%d_screenshot") fileName = builtin.GenNameWithTimestamp("%d_screenshot")
} }
var bufSource *bytes.Buffer var bufSource *bytes.Buffer
var imageResult *ai.ImageResult var imageResult *ai.CVResult
var imagePath string var imagePath string
var windowSize ai.Size var windowSize types.Size
var lastErr error var lastErr error
// get screenshot info with retry // get screenshot info with retry
for i := 0; i <= actionOptions.MaxRetryTimes; i++ { for i := 0; i < 3; i++ {
bufSource, imagePath, err = dExt.GetScreenShot(fileName) bufSource, imagePath, err = dExt.GetScreenShot(fileName)
if err != nil { if err != nil {
lastErr = err lastErr = err
@@ -85,9 +86,9 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
Tags: nil, Tags: nil,
Resolution: windowSize, Resolution: windowSize,
} }
imageResult, err = dExt.ImageService.GetImageFromBuffer(bufSource, opts...) imageResult, err = dExt.CVService.ReadFromBuffer(bufSource, opts...)
if err != nil { if err != nil {
log.Error().Err(err).Msg("GetImageFromBuffer from ImageService failed") log.Error().Err(err).Msg("ReadFromBuffer from ImageService failed")
lastErr = err lastErr = err
continue continue
} }
@@ -107,7 +108,7 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
screenResult.UploadedURL = imageResult.URL screenResult.UploadedURL = imageResult.URL
screenResult.Icons = imageResult.UIResult screenResult.Icons = imageResult.UIResult
if actionOptions.ScreenShotWithClosePopups && imageResult.ClosePopupsResult != nil { if screenshotOptions.ScreenShotWithClosePopups && imageResult.ClosePopupsResult != nil {
screenResult.Popup = &PopupInfo{ screenResult.Popup = &PopupInfo{
ClosePopupsResult: imageResult.ClosePopupsResult, ClosePopupsResult: imageResult.ClosePopupsResult,
PicName: imagePath, 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) { func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error) {
actionOptions := option.NewActionOptions(opts...) options := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" { if options.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName("get_screen_texts")) opts = append(opts, option.WithScreenShotFileName("get_screen_texts"))
} }
opts = append(opts, option.WithScreenShotOCR(true), option.WithScreenShotUpload(true)) 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) { func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) (point ai.PointF, err error) {
actionOptions := option.NewActionOptions(opts...) options := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" { if options.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text))) opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text)))
} }
ocrTexts, err := dExt.GetScreenTexts(opts...) ocrTexts, err := dExt.GetScreenTexts(opts...)
@@ -161,7 +162,7 @@ func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption)
return return
} }
result, err := ocrTexts.FindText(text, dExt.ParseActionOptions(opts...)...) result, err := ocrTexts.FindText(text, opts...)
if err != nil { if err != nil {
log.Warn().Msgf("FindText failed: %s", err.Error()) log.Warn().Msgf("FindText failed: %s", err.Error())
return 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) { func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.PointF, err error) {
actionOptions := option.NewActionOptions(opts...) options := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" { if options.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName( 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...) screenResult, err := dExt.GetScreenResult(opts...)
@@ -185,14 +186,14 @@ func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.Point
return return
} }
uiResults, err := screenResult.Icons.FilterUIResults(actionOptions.ScreenShotWithUITypes) uiResults, err := screenResult.Icons.FilterUIResults(options.ScreenShotWithUITypes)
if err != nil { if err != nil {
return return
} }
uiResult, err := uiResults.GetUIResult(dExt.ParseActionOptions(opts...)...) uiResult, err := uiResults.GetUIResult(opts...)
point = uiResult.Center() point = uiResult.Center()
log.Info().Interface("text", actionOptions.ScreenShotWithUITypes). log.Info().Interface("text", options.ScreenShotWithUITypes).
Interface("point", point).Msg("FindUIResult success") Interface("point", point).Msg("FindUIResult success")
return return
} }
+2 -2
View File
@@ -12,7 +12,7 @@ import (
"time" "time"
"github.com/httprunner/httprunner/v5/internal/json" "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"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@@ -25,7 +25,7 @@ type Session struct {
// cache to avoid repeated query // cache to avoid repeated query
scale float64 scale float64
windowSize ai.Size windowSize types.Size
// cache uia2/wda request and response // cache uia2/wda request and response
requests []*DriverRequests requests []*DriverRequests
+4 -4
View File
@@ -10,7 +10,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "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" "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)) 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...) actionOptions := option.NewActionOptions(opts...)
var swipeDirection interface{} var swipeDirection interface{}
@@ -177,11 +177,11 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOpti
return d.TapAbsXY(point.X, point.Y, opts...) 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...) 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 // go to home screen
if err := dExt.Driver.Homescreen(); err != nil { if err := dExt.Driver.Homescreen(); err != nil {
return errors.Wrap(err, "go to home screen failed") return errors.Wrap(err, "go to home screen failed")
+9 -7
View File
@@ -11,28 +11,30 @@ import (
func TestAndroidSwipeAction(t *testing.T) { func TestAndroidSwipeAction(t *testing.T) {
setupAndroidAdbDriver(t) setupAndroidAdbDriver(t)
swipeAction := driverExt.prepareSwipeAction("up", option.WithDirection("down")) dExt := driverExt.(*DriverExt)
err := swipeAction(driverExt) swipeAction := prepareSwipeAction(dExt, "up", option.WithDirection("down"))
err := swipeAction(dExt)
checkErr(t, err) checkErr(t, err)
swipeAction = driverExt.prepareSwipeAction("up", option.WithCustomDirection(0.5, 0.5, 0.5, 0.9)) swipeAction = prepareSwipeAction(dExt, "up", option.WithCustomDirection(0.5, 0.5, 0.5, 0.9))
err = swipeAction(driverExt) err = swipeAction(dExt)
checkErr(t, err) checkErr(t, err)
} }
func TestAndroidSwipeToTapApp(t *testing.T) { func TestAndroidSwipeToTapApp(t *testing.T) {
setupAndroidAdbDriver(t) setupAndroidAdbDriver(t)
err := driverExt.swipeToTapApp("抖音") err := driverExt.SwipeToTapApp("抖音")
checkErr(t, err) checkErr(t, err)
} }
func TestAndroidSwipeToTapTexts(t *testing.T) { func TestAndroidSwipeToTapTexts(t *testing.T) {
setupAndroidAdbDriver(t) setupAndroidAdbDriver(t)
err := driverExt.Driver.AppLaunch("com.ss.android.ugc.aweme") err := driverExt.GetDriver().AppLaunch("com.ss.android.ugc.aweme")
checkErr(t, err) checkErr(t, err)
err = driverExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, option.WithDirection("up")) dExt := driverExt.(*DriverExt)
err = dExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, option.WithDirection("up"))
checkErr(t, err) checkErr(t, err)
} }
+4 -4
View File
@@ -51,11 +51,11 @@ func (dExt *DriverExt) TapByOCR(ocrText string, opts ...option.ActionOption) err
} }
func (dExt *DriverExt) TapByUIDetection(opts ...option.ActionOption) error { func (dExt *DriverExt) TapByUIDetection(opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...) options := option.NewActionOptions(opts...)
point, err := dExt.FindUIResult(opts...) point, err := dExt.FindUIResult(opts...)
if err != nil { if err != nil {
if actionOptions.IgnoreNotFoundError { if options.IgnoreNotFoundError {
return nil return nil
} }
return err 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) { 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...) point, err := dExt.FindUIRectInUIKit(param, opts...)
if err != nil { if err != nil {
if actionOptions.IgnoreNotFoundError { if options.IgnoreNotFoundError {
return nil return nil
} }
return err return err
+10 -10
View File
@@ -3,20 +3,14 @@ package uixt
import ( import (
"testing" "testing"
"github.com/httprunner/httprunner/v5/pkg/ai" "github.com/httprunner/httprunner/v5/pkg/uixt/ai"
) )
func TestNewDriverExt(t *testing.T) { func TestNewDriverExt(t *testing.T) {
device, _ := NewAndroidDevice() device, _ := NewAndroidDevice()
var driver IDriver var driver IDriver
var err error var err error
if device.UIA2 || device.LogOn { driver, err = NewADBDriver(device)
driver, err = NewUIA2Driver(device)
} else if device.STUB {
driver, err = NewStubAndroidDriver(device)
} else {
driver, err = NewADBDriver(device)
}
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -24,6 +18,12 @@ func TestNewDriverExt(t *testing.T) {
driverExt, _ := NewDriverExt(driver, driverExt, _ := NewDriverExt(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM)) ai.WithCVService(ai.CVServiceTypeVEDEM))
driverExt.GetDriver() texts, _ := driverExt.GetScreenTexts()
t.Log(driverExt) t.Log(texts)
// get original dirver
driver = driverExt.GetDriver().(*ADBDriver)
// get device
device = driver.GetDevice().(*AndroidDevice)
} }
+57 -55
View File
@@ -1,19 +1,14 @@
package uixt package uixt
import ( import (
"fmt"
"code.byted.org/iesqa/ghdc" "code.byted.org/iesqa/ghdc"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) "github.com/httprunner/httprunner/v5/pkg/uixt/types"
var (
HdcServerHost = "localhost"
HdcServerPort = ghdc.HdcServerPort // 5037
) )
type HarmonyDevice struct { type HarmonyDevice struct {
@@ -23,65 +18,53 @@ type HarmonyDevice struct {
func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice, err error) { func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice, err error) {
deviceConfig := option.NewHarmonyDeviceOptions(opts...) 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 { 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 { // filter device by serial
return nil, errors.Wrap(code.DeviceConnectionError, "more than one device connected, please specify the serial") var harmonyDevice *ghdc.Device
}
dev := deviceList[0]
if deviceConfig.ConnectKey == "" { if deviceConfig.ConnectKey == "" {
selectSerial := dev.Serial() if len(devices) > 1 {
deviceConfig.ConnectKey = selectSerial return nil, errors.Wrap(code.DeviceConnectionError,
log.Warn(). "more than one device connected, please specify the serial")
Str("connectKey", deviceConfig.ConnectKey). }
Msg("harmony ConnectKey is not specified, select the first one") 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{ device = &HarmonyDevice{
HarmonyDeviceOptions: deviceConfig, HarmonyDeviceOptions: deviceConfig,
Device: dev, Device: harmonyDevice,
} }
log.Info().Str("connectKey", device.ConnectKey).Msg("init harmony device") log.Info().Str("connectKey", device.ConnectKey).Msg("init harmony device")
return device, nil 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 { func (dev *HarmonyDevice) Setup() error {
return nil return nil
} }
@@ -106,7 +89,26 @@ func (dev *HarmonyDevice) Uninstall(packageName string) error {
return nil 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") 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
} }
+21 -20
View File
@@ -10,8 +10,9 @@ import (
"code.byted.org/iesqa/ghdc" "code.byted.org/iesqa/ghdc"
"github.com/rs/zerolog/log" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
type HDCDriver struct { type HDCDriver struct {
@@ -58,31 +59,31 @@ func (hd *HDCDriver) GetSession() *Session {
return hd.Session return hd.Session
} }
func (hd *HDCDriver) Status() (DeviceStatus, error) { func (hd *HDCDriver) Status() (types.DeviceStatus, error) {
return DeviceStatus{}, errDriverNotImplemented return types.DeviceStatus{}, errDriverNotImplemented
} }
func (hd *HDCDriver) GetDevice() IDevice { func (hd *HDCDriver) GetDevice() IDevice {
return hd.HarmonyDevice return hd.HarmonyDevice
} }
func (hd *HDCDriver) DeviceInfo() (DeviceInfo, error) { func (hd *HDCDriver) DeviceInfo() (types.DeviceInfo, error) {
return DeviceInfo{}, errDriverNotImplemented return types.DeviceInfo{}, errDriverNotImplemented
} }
func (hd *HDCDriver) Location() (Location, error) { func (hd *HDCDriver) Location() (types.Location, error) {
return Location{}, errDriverNotImplemented return types.Location{}, errDriverNotImplemented
} }
func (hd *HDCDriver) BatteryInfo() (BatteryInfo, error) { func (hd *HDCDriver) BatteryInfo() (types.BatteryInfo, error) {
return BatteryInfo{}, errDriverNotImplemented 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() display, err := hd.uiDriver.GetDisplaySize()
if err != nil { if err != nil {
log.Error().Err(err).Msg("failed to get window size") log.Error().Err(err).Msg("failed to get window size")
return ai.Size{}, err return types.Size{}, err
} }
size.Width = display.Width size.Width = display.Width
size.Height = display.Height size.Height = display.Height
@@ -137,9 +138,9 @@ func (hd *HDCDriver) AppTerminate(packageName string) (bool, error) {
return true, nil return true, nil
} }
func (hd *HDCDriver) GetForegroundApp() (app AppInfo, err error) { func (hd *HDCDriver) GetForegroundApp() (app types.AppInfo, err error) {
// Todo // Todo
return AppInfo{}, errDriverNotImplemented return types.AppInfo{}, errDriverNotImplemented
} }
func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...string) error { func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...string) error {
@@ -155,8 +156,8 @@ func (hd *HDCDriver) StopCamera() error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *HDCDriver) Orientation() (orientation Orientation, err error) { func (hd *HDCDriver) Orientation() (orientation types.Orientation, err error) {
return OrientationPortrait, nil return types.OrientationPortrait, nil
} }
func (hd *HDCDriver) Tap(x, y float64, opts ...option.ActionOption) error { 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)) 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 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 return nil, errDriverNotImplemented
} }
@@ -237,7 +238,7 @@ func (hd *HDCDriver) Clear(packageName string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *HDCDriver) PressButton(devBtn DeviceButton) error { func (hd *HDCDriver) PressButton(devBtn types.DeviceButton) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
@@ -343,12 +344,12 @@ func (hd *HDCDriver) TearDown() error {
return nil return nil
} }
func (hd *HDCDriver) Rotation() (rotation Rotation, err error) { func (hd *HDCDriver) Rotation() (rotation types.Rotation, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
func (hd *HDCDriver) SetRotation(rotation Rotation) (err error) { func (hd *HDCDriver) SetRotation(rotation types.Rotation) (err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
+26 -64
View File
@@ -28,7 +28,9 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
var tunnelManager *tunnel.TunnelManager = nil var tunnelManager *tunnel.TunnelManager = nil
@@ -207,10 +209,10 @@ func (dev *IOSDevice) LogEnabled() bool {
return dev.LogOn 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) apps, err := dev.ListApps(ApplicationTypeAny)
if err != nil { if err != nil {
return AppInfo{}, err return types.AppInfo{}, err
} }
for _, app := range apps { for _, app := range apps {
if app.CFBundleIdentifier == packageName { if app.CFBundleIdentifier == packageName {
@@ -222,27 +224,18 @@ func (dev *IOSDevice) getAppInfo(packageName string) (appInfo AppInfo, err error
return appInfo, err 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) { func (dev *IOSDevice) NewDriver() (driverExt IDriverExt, err error) {
options := option.NewDriverOptions()
// init WDA driver
capabilities := options.Capabilities
if capabilities == nil {
capabilities = option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
}
var driver IDriver var driver IDriver
if dev.STUB { if dev.STUB {
driver, err = dev.NewStubDriver() driver, err = NewStubIOSDriver(dev)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to init Stub driver") return nil, errors.Wrap(err, "failed to init Stub driver")
} }
} else { } else {
driver, err = dev.NewHTTPDriver(capabilities) driver, err := NewWDADriver(dev)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to init WDA driver") 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...) settings, err := driver.SetAppiumSettings(map[string]interface{}{
if err != nil {
return nil, err
}
settings, err := driverExt.Driver.SetAppiumSettings(map[string]interface{}{
"snapshotMaxDepth": dev.SnapshotMaxDepth, "snapshotMaxDepth": dev.SnapshotMaxDepth,
"acceptAlertButtonSelector": dev.AcceptAlertButtonSelector, "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") log.Info().Interface("appiumWDASettings", settings).Msg("set appium WDA settings")
if dev.LogOn { if dev.LogOn {
err = driverExt.Driver.StartCaptureLog("hrp_wda_log") err = driver.StartCaptureLog("hrp_wda_log")
if err != nil { if err != nil {
return nil, err 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 return driverExt, nil
} }
@@ -587,52 +585,16 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID
return wd, nil return wd, nil
} }
const ( func (dev *IOSDevice) GetPackageInfo(packageName string) (types.AppInfo, error) {
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) {
svc, err := installationproxy.New(dev.d) svc, err := installationproxy.New(dev.d)
if err != nil { if err != nil {
return AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error()) return types.AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error())
} }
defer svc.Close() defer svc.Close()
apps, err := svc.BrowseAllApps() apps, err := svc.BrowseAllApps()
if err != nil { if err != nil {
return AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error()) return types.AppInfo{}, errors.Wrap(code.DeviceGetInfoError, err.Error())
} }
for _, app := range apps { for _, app := range apps {
@@ -640,9 +602,9 @@ func (dev *IOSDevice) GetPackageInfo(packageName string) (AppInfo, error) {
continue continue
} }
appInfo := AppInfo{ appInfo := types.AppInfo{
Name: app.CFBundleName, Name: app.CFBundleName,
AppBaseInfo: AppBaseInfo{ AppBaseInfo: types.AppBaseInfo{
BundleId: app.CFBundleIdentifier, BundleId: app.CFBundleIdentifier,
PackageName: app.CFBundleIdentifier, PackageName: app.CFBundleIdentifier,
VersionName: app.CFBundleShortVersionString, VersionName: app.CFBundleShortVersionString,
@@ -654,6 +616,6 @@ func (dev *IOSDevice) GetPackageInfo(packageName string) (AppInfo, error) {
log.Info().Interface("appInfo", appInfo).Msg("get package info") log.Info().Interface("appInfo", appInfo).Msg("get package info")
return appInfo, nil return appInfo, nil
} }
return AppInfo{}, errors.Wrap(code.DeviceAppNotInstalled, return types.AppInfo{}, errors.Wrap(code.DeviceAppNotInstalled,
fmt.Sprintf("%s not found", packageName)) fmt.Sprintf("%s not found", packageName))
} }
+102 -73
View File
@@ -7,13 +7,58 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log" "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/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 *WDADriver
bightInsightPrefix string bightInsightPrefix string
@@ -22,23 +67,7 @@ type stubIOSDriver struct {
device *IOSDevice device *IOSDevice
} }
func newStubIOSDriver(bightInsightAddr, serverAddr string, dev *IOSDevice, readTimeout ...time.Duration) (*stubIOSDriver, error) { func (s *StubIOSDriver) setUpWda() (err 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) {
if s.WDADriver == nil { if s.WDADriver == nil {
capabilities := option.NewCapabilities() capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept) capabilities.WithDefaultAlertAction(option.AlertActionAccept)
@@ -53,7 +82,7 @@ func (s *stubIOSDriver) setUpWda() (err error) {
} }
// NewSession starts a new session and returns the DriverSession. // 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() err := s.setUpWda()
if err != nil { if err != nil {
return Session{}, err 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 // DeleteSession Kills application associated with that session and removes session
// 1. alertsMonitor disable // 1. alertsMonitor disable
// 2. testedApplicationBundleId terminate // 2. testedApplicationBundleId terminate
func (s *stubIOSDriver) DeleteSession() error { func (s *StubIOSDriver) DeleteSession() error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -72,34 +101,34 @@ func (s *stubIOSDriver) DeleteSession() error {
return s.WDADriver.DeleteSession() return s.WDADriver.DeleteSession()
} }
func (s *stubIOSDriver) Status() (DeviceStatus, error) { func (s *StubIOSDriver) Status() (types.DeviceStatus, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return DeviceStatus{}, err return types.DeviceStatus{}, err
} }
return s.WDADriver.Status() return s.WDADriver.Status()
} }
func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) { func (s *StubIOSDriver) DeviceInfo() (types.DeviceInfo, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return DeviceInfo{}, err return types.DeviceInfo{}, err
} }
return s.WDADriver.DeviceInfo() return s.WDADriver.DeviceInfo()
} }
func (s *stubIOSDriver) Location() (Location, error) { func (s *StubIOSDriver) Location() (types.Location, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return Location{}, err return types.Location{}, err
} }
return s.WDADriver.Location() return s.WDADriver.Location()
} }
func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) { func (s *StubIOSDriver) BatteryInfo() (types.BatteryInfo, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return BatteryInfo{}, err return types.BatteryInfo{}, err
} }
return s.WDADriver.BatteryInfo() return s.WDADriver.BatteryInfo()
} }
@@ -107,15 +136,15 @@ func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) {
// WindowSize Return the width and height in portrait mode. // 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, // when getting the window size in wda/ui2/adb, if the device is in landscape mode,
// the width and height will be reversed. // the width and height will be reversed.
func (s *stubIOSDriver) WindowSize() (ai.Size, error) { func (s *StubIOSDriver) WindowSize() (types.Size, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return ai.Size{}, err return types.Size{}, err
} }
return s.WDADriver.WindowSize() return s.WDADriver.WindowSize()
} }
func (s *stubIOSDriver) Screen() (ai.Screen, error) { func (s *StubIOSDriver) Screen() (ai.Screen, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return ai.Screen{}, err return ai.Screen{}, err
@@ -123,7 +152,7 @@ func (s *stubIOSDriver) Screen() (ai.Screen, error) {
return s.WDADriver.Screen() return s.WDADriver.Screen()
} }
func (s *stubIOSDriver) Scale() (float64, error) { func (s *StubIOSDriver) Scale() (float64, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return 0, err 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 // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -140,7 +169,7 @@ func (s *stubIOSDriver) Homescreen() error {
return s.WDADriver.Homescreen() return s.WDADriver.Homescreen()
} }
func (s *stubIOSDriver) Unlock() (err error) { func (s *StubIOSDriver) Unlock() (err error) {
err = s.setUpWda() err = s.setUpWda()
if err != nil { if err != nil {
return err 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. // AppLaunch Launch an application with given bundle identifier in scope of current session.
// !This method is only available since Xcode9 SDK // !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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -160,7 +189,7 @@ func (s *stubIOSDriver) AppLaunch(packageName string) error {
// AppTerminate Terminate an application with the given package name. // 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 // 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() err := s.setUpWda()
if err != nil { if err != nil {
return false, err 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 // 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() err = s.setUpWda()
if err != nil { if err != nil {
return AppInfo{}, err return types.AppInfo{}, err
} }
return s.WDADriver.GetForegroundApp() return s.WDADriver.GetForegroundApp()
} }
// StartCamera Starts a new camera for recording // StartCamera Starts a new camera for recording
func (s *stubIOSDriver) StartCamera() error { func (s *StubIOSDriver) StartCamera() error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -187,7 +216,7 @@ func (s *stubIOSDriver) StartCamera() error {
} }
// StopCamera Stops the camera for recording // StopCamera Stops the camera for recording
func (s *stubIOSDriver) StopCamera() error { func (s *StubIOSDriver) StopCamera() error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -195,16 +224,16 @@ func (s *stubIOSDriver) StopCamera() error {
return s.WDADriver.StopCamera() return s.WDADriver.StopCamera()
} }
func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) { func (s *StubIOSDriver) Orientation() (orientation types.Orientation, err error) {
err = s.setUpWda() err = s.setUpWda()
if err != nil { if err != nil {
return OrientationPortrait, err return types.OrientationPortrait, err
} }
return s.WDADriver.Orientation() return s.WDADriver.Orientation()
} }
// Tap Sends a tap event at the coordinate. // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err 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. // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err 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. // TouchAndHold Initiates a long-press gesture at the coordinate, holding for the specified duration.
// //
// second: The default value is 1 // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err 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. // 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). // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err 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 // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -254,7 +283,7 @@ func (s *stubIOSDriver) SetPasteboard(contentType PasteboardType, content string
// GetPasteboard Gets the data contained in the general pasteboard. // GetPasteboard Gets the data contained in the general pasteboard.
// //
// It worked when `WDA` was foreground. https://github.com/appium/WebDriverAgent/issues/330 // 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() err = s.setUpWda()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -262,7 +291,7 @@ func (s *stubIOSDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Bu
return s.WDADriver.GetPasteboard(contentType) return s.WDADriver.GetPasteboard(contentType)
} }
func (s *stubIOSDriver) SetIme(ime string) error { func (s *StubIOSDriver) SetIme(ime string) error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err 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, // SendKeys Types a string into active element. There must be element with keyboard focus,
// otherwise an error is raised. // otherwise an error is raised.
// WithFrequency option can be used to set frequency of typing (letters per sec). The default value is 60 // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -282,7 +311,7 @@ func (s *stubIOSDriver) SendKeys(text string, opts ...option.ActionOption) error
} }
// Input works like SendKeys // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -290,7 +319,7 @@ func (s *stubIOSDriver) Input(text string, opts ...option.ActionOption) error {
return s.WDADriver.Input(text, opts...) return s.WDADriver.Input(text, opts...)
} }
func (s *stubIOSDriver) Clear(packageName string) error { func (s *StubIOSDriver) Clear(packageName string) error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -299,7 +328,7 @@ func (s *stubIOSDriver) Clear(packageName string) error {
} }
// PressButton Presses the corresponding hardware button on the device // 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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -308,7 +337,7 @@ func (s *stubIOSDriver) PressButton(devBtn DeviceButton) error {
} }
// PressBack Presses the back button // PressBack Presses the back button
func (s *stubIOSDriver) PressBack(opts ...option.ActionOption) error { func (s *StubIOSDriver) PressBack(opts ...option.ActionOption) error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -316,7 +345,7 @@ func (s *stubIOSDriver) PressBack(opts ...option.ActionOption) error {
return s.WDADriver.PressBack(opts...) return s.WDADriver.PressBack(opts...)
} }
func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { func (s *StubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
err = s.setUpWda() err = s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -324,7 +353,7 @@ func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
return s.WDADriver.PressKeyCode(keyCode) return s.WDADriver.PressKeyCode(keyCode)
} }
func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) { func (s *StubIOSDriver) Screenshot() (*bytes.Buffer, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -345,7 +374,7 @@ func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) {
//return bytes.NewBuffer(imageBytes), nil //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() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -353,7 +382,7 @@ func (s *stubIOSDriver) TapByText(text string, opts ...option.ActionOption) erro
return s.WDADriver.TapByText(text, opts...) return s.WDADriver.TapByText(text, opts...)
} }
func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error { func (s *StubIOSDriver) TapByTexts(actions ...TapTextAction) error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -362,7 +391,7 @@ func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error {
} }
// AccessibleSource Return application elements accessibility tree // AccessibleSource Return application elements accessibility tree
func (s *stubIOSDriver) AccessibleSource() (string, error) { func (s *StubIOSDriver) AccessibleSource() (string, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return "", err return "", err
@@ -375,7 +404,7 @@ func (s *stubIOSDriver) AccessibleSource() (string, error) {
// Checks health of XCTest by: // Checks health of XCTest by:
// 1) Querying application for some elements, // 1) Querying application for some elements,
// 2) Triggering some device events. // 2) Triggering some device events.
func (s *stubIOSDriver) HealthCheck() error { func (s *StubIOSDriver) HealthCheck() error {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -383,7 +412,7 @@ func (s *stubIOSDriver) HealthCheck() error {
return s.WDADriver.HealthCheck() return s.WDADriver.HealthCheck()
} }
func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { func (s *StubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -391,7 +420,7 @@ func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
return s.WDADriver.GetAppiumSettings() 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() err := s.setUpWda()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -399,7 +428,7 @@ func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[
return s.WDADriver.SetAppiumSettings(settings) return s.WDADriver.SetAppiumSettings(settings)
} }
func (s *stubIOSDriver) IsHealthy() (bool, error) { func (s *StubIOSDriver) IsHealthy() (bool, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return false, err return false, err
@@ -408,7 +437,7 @@ func (s *stubIOSDriver) IsHealthy() (bool, error) {
} }
// triggers the log capture and returns the log entries // 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() err = s.setUpWda()
if err != nil { if err != nil {
return err return err
@@ -416,7 +445,7 @@ func (s *stubIOSDriver) StartCaptureLog(identifier ...string) (err error) {
return s.WDADriver.StartCaptureLog(identifier...) return s.WDADriver.StartCaptureLog(identifier...)
} }
func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) { func (s *StubIOSDriver) StopCaptureLog() (result interface{}, err error) {
err = s.setUpWda() err = s.setUpWda()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -424,7 +453,7 @@ func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) {
return s.WDADriver.StopCaptureLog() return s.WDADriver.StopCaptureLog()
} }
func (s *stubIOSDriver) GetDriverResults() []*DriverRequests { func (s *StubIOSDriver) GetDriverResults() []*DriverRequests {
err := s.setUpWda() err := s.setUpWda()
if err != nil { if err != nil {
return nil return nil
@@ -432,7 +461,7 @@ func (s *stubIOSDriver) GetDriverResults() []*DriverRequests {
return s.WDADriver.GetDriverResults() 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{}) resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/source?format=json&onlyWeb=false", s.bightInsightPrefix), []byte{})
if err != nil { if err != nil {
return "", err return "", err
@@ -440,7 +469,7 @@ func (s *stubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) {
return string(resp), nil 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{}{ params := map[string]interface{}{
"phone": phoneNumber, "phone": phoneNumber,
} }
@@ -478,7 +507,7 @@ func (s *stubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, pa
return info, nil 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{}) resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/host/loginout/", s.serverPrefix), []byte{})
if err != nil { if err != nil {
return err return err
@@ -497,12 +526,12 @@ func (s *stubIOSDriver) LogoutNoneUI(packageName string) error {
return nil return nil
} }
func (s *stubIOSDriver) TearDown() error { func (s *StubIOSDriver) TearDown() error {
s.client.CloseIdleConnections() s.client.CloseIdleConnections()
return nil 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{}) resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/host/app/info/", s.serverPrefix), []byte{})
if err != nil { if err != nil {
return info, err return info, err
@@ -524,6 +553,6 @@ func (s *stubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo,
return info, nil return info, nil
} }
func (s *stubIOSDriver) GetSession() *Session { func (s *StubIOSDriver) GetSession() *Session {
return s.Session return s.Session
} }
+10 -10
View File
@@ -11,7 +11,7 @@ import (
) )
var ( var (
iOSStubDriver IDriver iOSStubDriver IDriverExt
iOSDevice *IOSDevice iOSDevice *IOSDevice
) )
@@ -22,56 +22,56 @@ func setupiOSStubDriver(t *testing.T) {
option.WithWDAMjpegPort(8800), option.WithWDAMjpegPort(8800),
option.WithIOSStub(false)) option.WithIOSStub(false))
checkErr(t, err) checkErr(t, err)
iOSStubDriver, err = iOSDevice.NewStubDriver() iOSStubDriver, err = iOSDevice.NewDriver()
checkErr(t, err) checkErr(t, err)
} }
func TestIOSLogin(t *testing.T) { func TestIOSLogin(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
info, err := iOSStubDriver.LoginNoneUI("", "12342316231", "8517", "") info, err := iOSStubDriver.GetDriver().LoginNoneUI("", "12342316231", "8517", "")
checkErr(t, err) checkErr(t, err)
t.Log(info) t.Log(info)
} }
func TestIOSLogout(t *testing.T) { func TestIOSLogout(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
err := iOSStubDriver.LogoutNoneUI("") err := iOSStubDriver.GetDriver().LogoutNoneUI("")
checkErr(t, err) checkErr(t, err)
} }
func TestIOSIsLogin(t *testing.T) { func TestIOSIsLogin(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
err := iOSStubDriver.LogoutNoneUI("") err := iOSStubDriver.GetDriver().LogoutNoneUI("")
checkErr(t, err) checkErr(t, err)
} }
func TestIOSSource(t *testing.T) { func TestIOSSource(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
source, err := iOSStubDriver.Source() source, err := iOSStubDriver.GetDriver().Source()
checkErr(t, err) checkErr(t, err)
t.Log(source) t.Log(source)
} }
func TestIOSForeground(t *testing.T) { func TestIOSForeground(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
app, err := iOSStubDriver.GetForegroundApp() app, err := iOSStubDriver.GetDriver().GetForegroundApp()
checkErr(t, err) checkErr(t, err)
t.Log(app) t.Log(app)
} }
func TestIOSSwipe(t *testing.T) { func TestIOSSwipe(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
iOSStubDriver.Swipe(540, 0, 540, 1000) iOSStubDriver.GetDriver().Swipe(540, 0, 540, 1000)
} }
func TestIOSSave(t *testing.T) { func TestIOSSave(t *testing.T) {
setupiOSStubDriver(t) setupiOSStubDriver(t)
raw, err := iOSStubDriver.Screenshot() raw, err := iOSStubDriver.GetDriver().Screenshot()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
source, err := iOSStubDriver.Source() source, err := iOSStubDriver.GetDriver().Source()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+43 -42
View File
@@ -23,8 +23,9 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json" "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/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
func NewWDADriver(device *IOSDevice) (*WDADriver, error) { func NewWDADriver(device *IOSDevice) (*WDADriver, error) {
@@ -161,16 +162,16 @@ func (wd *WDADriver) DeleteSession() (err error) {
return 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:)] // [[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)]
var rawResp rawResponse var rawResp rawResponse
// Notice: use Driver.GET instead of httpGET to avoid loop calling // Notice: use Driver.GET instead of httpGET to avoid loop calling
if rawResp, err = wd.GET("/status"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return DeviceStatus{}, err return types.DeviceStatus{}, err
} }
deviceStatus = reply.Value.DeviceStatus deviceStatus = reply.Value.DeviceStatus
return return
@@ -180,51 +181,51 @@ func (wd *WDADriver) GetDevice() IDevice {
return wd.IOSDevice 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"] respondWithTarget:self action:@selector(handleGetDeviceInfo:)]
// [[FBRoute GET:@"/wda/device/info"].withoutSession // [[FBRoute GET:@"/wda/device/info"].withoutSession
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/device/info"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return DeviceInfo{}, err return types.DeviceInfo{}, err
} }
deviceInfo = reply.Value.DeviceInfo deviceInfo = reply.Value.DeviceInfo
return 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"] respondWithTarget:self action:@selector(handleGetLocation:)]
// [[FBRoute GET:@"/wda/device/location"].withoutSession // [[FBRoute GET:@"/wda/device/location"].withoutSession
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/device/location"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return Location{}, err return types.Location{}, err
} }
location = reply.Value.Location location = reply.Value.Location
return 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:)] // [[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/batteryInfo"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return BatteryInfo{}, err return types.BatteryInfo{}, err
} }
batteryInfo = reply.Value.BatteryInfo batteryInfo = reply.Value.BatteryInfo
return 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:)] // [[FBRoute GET:@"/window/size"] respondWithTarget:self action:@selector(handleGetWindowSize:)]
if !wd.windowSize.IsNil() { if !wd.windowSize.IsNil() {
// use cached window size // use cached window size
@@ -233,16 +234,16 @@ func (wd *WDADriver) WindowSize() (size ai.Size, err error) {
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/window/size"); err != nil { 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 { 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 size = reply.Value.Size
scale, err := wd.Scale() scale, err := wd.Scale()
if err != nil { 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.Height = size.Height * int(scale)
size.Width = size.Width * int(scale) size.Width = size.Width * int(scale)
@@ -281,28 +282,28 @@ func (wd *WDADriver) toScale(x float64) float64 {
return x / wd.scale 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"] respondWithTarget:self action:@selector(handleActiveAppInfo:)]
// [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession // [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/activeAppInfo"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return AppInfo{}, err return types.AppInfo{}, err
} }
info = reply.Value.AppInfo info = reply.Value.AppInfo
return 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:)] // [[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil {
return nil, err return nil, err
} }
reply := new(struct{ Value []AppBaseInfo }) reply := new(struct{ Value []types.AppBaseInfo })
if err = json.Unmarshal(rawResp, reply); err != nil { if err = json.Unmarshal(rawResp, reply); err != nil {
return nil, err return nil, err
} }
@@ -310,14 +311,14 @@ func (wd *WDADriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
return 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:)] // [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)]
data := map[string]interface{}{"bundleId": bundleId} data := map[string]interface{}{"bundleId": bundleId}
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/state"); err != nil { if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/state"); err != nil {
return 0, err return 0, err
} }
reply := new(struct{ Value AppState }) reply := new(struct{ Value types.AppState })
if err = json.Unmarshal(rawResp, reply); err != nil { if err = json.Unmarshal(rawResp, reply); err != nil {
return 0, err return 0, err
} }
@@ -471,7 +472,7 @@ func (wd *WDADriver) AppDeactivate(second float64) (err error) {
return return
} }
func (wd *WDADriver) GetForegroundApp() (appInfo AppInfo, err error) { func (wd *WDADriver) GetForegroundApp() (appInfo types.AppInfo, err error) {
activeAppInfo, err := wd.ActiveAppInfo() activeAppInfo, err := wd.ActiveAppInfo()
appInfo.BundleId = activeAppInfo.BundleId appInfo.BundleId = activeAppInfo.BundleId
if err != nil { 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...) 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:)] // [[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)]
data := map[string]interface{}{ data := map[string]interface{}{
"contentType": contentType, "contentType": contentType,
@@ -621,7 +622,7 @@ func (wd *WDADriver) SetPasteboard(contentType PasteboardType, content string) (
return 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:)] // [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)]
data := map[string]interface{}{"contentType": contentType} data := map[string]interface{}{"contentType": contentType}
var rawResp rawResponse var rawResp rawResponse
@@ -713,7 +714,7 @@ func (wd *WDADriver) PressBack(opts ...option.ActionOption) (err error) {
return 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:)] // [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)]
data := map[string]interface{}{"name": devBtn} data := map[string]interface{}{"name": devBtn}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton") _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton")
@@ -745,13 +746,13 @@ func (wd *WDADriver) StopCamera() (err error) {
return nil 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:)] // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil {
return "", err return "", err
} }
reply := new(struct{ Value Orientation }) reply := new(struct{ Value types.Orientation })
if err = json.Unmarshal(rawResp, reply); err != nil { if err = json.Unmarshal(rawResp, reply); err != nil {
return "", err return "", err
} }
@@ -759,28 +760,28 @@ func (wd *WDADriver) Orientation() (orientation Orientation, err error) {
return 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:)] // [[FBRoute POST:@"/orientation"] respondWithTarget:self action:@selector(handleSetOrientation:)]
data := map[string]interface{}{"orientation": orientation} data := map[string]interface{}{"orientation": orientation}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation") _, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation")
return 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:)] // [[FBRoute GET:@"/rotation"] respondWithTarget:self action:@selector(handleGetRotation:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/rotation"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return Rotation{}, err return types.Rotation{}, err
} }
rotation = reply.Value rotation = reply.Value
return 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:)] // [[FBRoute POST:@"/rotation"] respondWithTarget:self action:@selector(handleSetRotation:)]
_, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation") _, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation")
return return
+9 -12
View File
@@ -11,12 +11,13 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
) )
var ( var (
bundleId = "com.apple.Preferences" bundleId = "com.apple.Preferences"
driver IDriver driver IDriver
iOSDriverExt *DriverExt iOSDriverExt IDriverExt
) )
func setup(t *testing.T) { func setup(t *testing.T) {
@@ -29,11 +30,7 @@ func setup(t *testing.T) {
} }
capabilities := option.NewCapabilities() capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept) capabilities.WithDefaultAlertAction(option.AlertActionAccept)
driver, err = device.NewHTTPDriver(capabilities) iOSDriverExt, err = device.NewDriver()
if err != nil {
t.Fatal(err)
}
iOSDriverExt, err = newDriverExt(device, driver)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -46,7 +43,7 @@ func TestViaUSB(t *testing.T) {
func TestInstall(t *testing.T) { func TestInstall(t *testing.T) {
setup(t) setup(t)
err := iOSDriverExt.Install("xxx.ipa", err := iOSDriverExt.GetDriver().GetDevice().Install("xxx.ipa",
option.WithRetryTimes(5)) option.WithRetryTimes(5))
log.Error().Err(err) log.Error().Err(err)
if err != nil { if err != nil {
@@ -320,7 +317,7 @@ func Test_remoteWD_SetPasteboard(t *testing.T) {
setup(t) setup(t)
// err := driver.SetPasteboard(PasteboardTypePlaintext, "gwda") // err := driver.SetPasteboard(PasteboardTypePlaintext, "gwda")
err := driver.SetPasteboard(PasteboardTypeUrl, "Clock-stopwatch://") err := driver.SetPasteboard(types.PasteboardTypeUrl, "Clock-stopwatch://")
// userHomeDir, _ := os.UserHomeDir() // userHomeDir, _ := os.UserHomeDir()
// bytesImg, _ := ioutil.ReadFile(userHomeDir + "/Pictures/IMG_0806.jpg") // bytesImg, _ := ioutil.ReadFile(userHomeDir + "/Pictures/IMG_0806.jpg")
// err := driver.SetPasteboard(PasteboardTypeImage, string(bytesImg)) // err := driver.SetPasteboard(PasteboardTypeImage, string(bytesImg))
@@ -335,7 +332,7 @@ func Test_remoteWD_GetPasteboard(t *testing.T) {
var buffer *bytes.Buffer var buffer *bytes.Buffer
var err error var err error
buffer, err = driver.GetPasteboard(PasteboardTypePlaintext) buffer, err = driver.GetPasteboard(types.PasteboardTypePlaintext)
// buffer, err = driver.GetPasteboard(PasteboardTypeUrl) // buffer, err = driver.GetPasteboard(PasteboardTypeUrl)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -367,17 +364,17 @@ func Test_remoteWD_SendKeys(t *testing.T) {
func Test_remoteWD_PressButton(t *testing.T) { func Test_remoteWD_PressButton(t *testing.T) {
setup(t) setup(t)
err := driver.PressButton(DeviceButtonVolumeUp) err := driver.PressButton(types.DeviceButtonVolumeUp)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
err = driver.PressButton(DeviceButtonVolumeDown) err = driver.PressButton(types.DeviceButtonVolumeDown)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
err = driver.PressButton(DeviceButtonHome) err = driver.PressButton(types.DeviceButtonHome)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+11 -179
View File
@@ -6,54 +6,24 @@ import (
"github.com/httprunner/httprunner/v5/internal/builtin" "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 { type ActionOptions struct {
// log // log
Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // used to identify the action in log Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // used to identify the action in log
// control related // control related
MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times 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
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
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
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
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
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
Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action Frequency int `json:"frequency,omitempty" yaml:"frequency,omitempty"`
Frequency int `json:"frequency,omitempty" yaml:"frequency,omitempty"`
// scope related ScreenOptions
Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"`
AbsScope AbsScope `json:"abs_scope,omitempty" yaml:"abs_scope,omitempty"`
Regex bool `json:"regex,omitempty" yaml:"regex,omitempty"` // use regex to match text
Offset []int `json:"offset,omitempty" yaml:"offset,omitempty"` // used to tap offset of point
OffsetRandomRange []int `json:"offset_random_range,omitempty" yaml:"offset_random_range,omitempty"` // set random range [min, max] for tap/swipe points
Index int `json:"index,omitempty" yaml:"index,omitempty"` // index of the target element
MatchOne bool `json:"match_one,omitempty" yaml:"match_one,omitempty"` // match one of the targets if existed
// set custiom options such as textview, id, description // set custiom options such as textview, id, description
Custom map[string]interface{} `json:"custom,omitempty" yaml:"custom,omitempty"` 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 { 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 return options
} }
func (o *ActionOptions) ScreenshotActions() []string { func (o *ActionOptions) GetScreenOptions() []ActionOption {
actions := []string{} return o.ScreenOptions.Options()
if o.ScreenShotWithUpload {
actions = append(actions, "upload")
}
if o.ScreenShotWithOCR {
actions = append(actions, "ocr")
}
if o.ScreenShotWithLiveType {
actions = append(actions, "liveType")
}
if o.ScreenShotWithLivePopularity {
actions = append(actions, "livePopularity")
}
// UI detection
if len(o.ScreenShotWithUITypes) > 0 {
actions = append(actions, "ui")
}
if o.ScreenShotWithClosePopups {
actions = append(actions, "close")
}
return actions
} }
func (o *ActionOptions) GetRandomOffset() float64 { 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 // set alias for compatibility
var WithWaitTime = WithInterval 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] // swipe [fromX, fromY, toX, toY] with offset [offsetFromX, offsetFromY, offsetToX, offsetToY]
func WithSwipeOffset(offsetFromX, offsetFromY, offsetToX, offsetToY int) ActionOption { func WithSwipeOffset(offsetFromX, offsetFromY, offsetToX, offsetToY int) ActionOption {
return func(o *ActionOptions) { 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 { func WithFrequency(frequency int) ActionOption {
return func(o *ActionOptions) { return func(o *ActionOptions) {
o.Frequency = frequency o.Frequency = frequency
@@ -404,51 +284,3 @@ func WithIgnoreNotFoundError(ignoreError bool) ActionOption {
o.IgnoreNotFoundError = ignoreError 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
}
}
-49
View File
@@ -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
}
}
+7
View File
@@ -1,5 +1,12 @@
package option package option
import "code.byted.org/iesqa/ghdc"
const (
HdcServerHost = "localhost"
HdcServerPort = ghdc.HdcServerPort // 5037
)
type HarmonyDeviceOptions struct { type HarmonyDeviceOptions struct {
ConnectKey string `json:"connect_key,omitempty" yaml:"connect_key,omitempty"` ConnectKey string `json:"connect_key,omitempty" yaml:"connect_key,omitempty"`
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"` LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
+46 -50
View File
@@ -1,11 +1,10 @@
package ai package option
func NewScreenShotOptions(opts ...ScreenShotOption) *ScreenShotOptions { import "github.com/httprunner/httprunner/v5/pkg/uixt/types"
options := &ScreenShotOptions{}
for _, option := range opts { type ScreenOptions struct {
option(options) ScreenShotOptions
} ScreenFilterOptions
return options
} }
type ScreenShotOptions struct { type ScreenShotOptions struct {
@@ -19,8 +18,8 @@ type ScreenShotOptions struct {
ScreenShotFileName string `json:"screenshot_file_name,omitempty" yaml:"screenshot_file_name,omitempty"` ScreenShotFileName string `json:"screenshot_file_name,omitempty" yaml:"screenshot_file_name,omitempty"`
} }
func (o *ScreenShotOptions) Options() []ScreenShotOption { func (o *ScreenShotOptions) Options() []ActionOption {
options := make([]ScreenShotOption, 0) options := make([]ActionOption, 0)
if o == nil { if o == nil {
return options return options
} }
@@ -78,52 +77,50 @@ func (o *ScreenShotOptions) List() []string {
return options return options
} }
type ScreenShotOption func(o *ScreenShotOptions) func WithScreenShotOCR(ocrOn bool) ActionOption {
return func(o *ActionOptions) {
func WithScreenShotOCR(ocrOn bool) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithOCR = ocrOn o.ScreenShotWithOCR = ocrOn
} }
} }
func WithScreenShotUpload(uploadOn bool) ScreenShotOption { func WithScreenShotUpload(uploadOn bool) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotWithUpload = uploadOn o.ScreenShotWithUpload = uploadOn
} }
} }
func WithScreenShotLiveType(liveTypeOn bool) ScreenShotOption { func WithScreenShotLiveType(liveTypeOn bool) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotWithLiveType = liveTypeOn o.ScreenShotWithLiveType = liveTypeOn
} }
} }
func WithScreenShotLivePopularity(livePopularityOn bool) ScreenShotOption { func WithScreenShotLivePopularity(livePopularityOn bool) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotWithLivePopularity = livePopularityOn o.ScreenShotWithLivePopularity = livePopularityOn
} }
} }
func WithScreenShotUITypes(uiTypes ...string) ScreenShotOption { func WithScreenShotUITypes(uiTypes ...string) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotWithUITypes = uiTypes o.ScreenShotWithUITypes = uiTypes
} }
} }
func WithScreenShotClosePopups(closeOn bool) ScreenShotOption { func WithScreenShotClosePopups(closeOn bool) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotWithClosePopups = closeOn o.ScreenShotWithClosePopups = closeOn
} }
} }
func WithScreenOCRCluster(ocrCluster string) ScreenShotOption { func WithScreenOCRCluster(ocrCluster string) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotWithOCRCluster = ocrCluster o.ScreenShotWithOCRCluster = ocrCluster
} }
} }
func WithScreenShotFileName(fileName string) ScreenShotOption { func WithScreenShotFileName(fileName string) ActionOption {
return func(o *ScreenShotOptions) { return func(o *ActionOptions) {
o.ScreenShotFileName = fileName o.ScreenShotFileName = fileName
} }
} }
@@ -132,7 +129,7 @@ func WithScreenShotFileName(fileName string) ScreenShotOption {
// [x1, y1, x2, y2] in percentage of the screen // [x1, y1, x2, y2] in percentage of the screen
type Scope []float64 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] x1, y1, x2, y2 := s[0], s[1], s[2], s[3]
// convert relative scope to absolute scope // convert relative scope to absolute scope
absX1 := int(x1 * float64(windowSize.Width)) absX1 := int(x1 * float64(windowSize.Width))
@@ -145,12 +142,12 @@ func (s Scope) ToAbs(windowSize Size) AbsScope {
// [x1, y1, x2, y2] in absolute pixels // [x1, y1, x2, y2] in absolute pixels
type AbsScope []int type AbsScope []int
func (s AbsScope) Option() ScreenFilterOption { func (s AbsScope) Option() ActionOption {
return WithAbsScope(s[0], s[1], s[2], s[3]) return WithAbsScope(s[0], s[1], s[2], s[3])
} }
func NewScreenFilterOptions(opts ...ScreenFilterOption) *ScreenFilterOptions { func NewScreenFilterOptions(opts ...ActionOption) *ActionOptions {
options := &ScreenFilterOptions{} options := &ActionOptions{}
for _, option := range opts { for _, option := range opts {
option(options) option(options)
} }
@@ -162,52 +159,51 @@ type ScreenFilterOptions struct {
Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"` Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"`
AbsScope AbsScope `json:"abs_scope,omitempty" yaml:"abs_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 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 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 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 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 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)] // 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 // 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 { func WithScope(x1, y1, x2, y2 float64) ActionOption {
return func(o *ScreenFilterOptions) { return func(o *ActionOptions) {
o.Scope = Scope{x1, y1, x2, y2} o.Scope = Scope{x1, y1, x2, y2}
} }
} }
// WithAbsScope inputs area of [(x1,y1), (x2,y2)] // WithAbsScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all absolute position of the screen // x1, y1, x2, y2 are all absolute position of the screen
func WithAbsScope(x1, y1, x2, y2 int) ScreenFilterOption { func WithAbsScope(x1, y1, x2, y2 int) ActionOption {
return func(o *ScreenFilterOptions) { return func(o *ActionOptions) {
o.AbsScope = AbsScope{x1, y1, x2, y2} o.AbsScope = AbsScope{x1, y1, x2, y2}
} }
} }
// tap [x, y] with offset [offsetX, offsetY] // tap [x, y] with offset [offsetX, offsetY]
func WithTapOffset(offsetX, offsetY int) ScreenFilterOption { func WithTapOffset(offsetX, offsetY int) ActionOption {
return func(o *ScreenFilterOptions) { return func(o *ActionOptions) {
o.Offset = []int{offsetX, offsetY} o.Offset = []int{offsetX, offsetY}
} }
} }
func WithRegex(regex bool) ScreenFilterOption { func WithRegex(regex bool) ActionOption {
return func(o *ScreenFilterOptions) { return func(o *ActionOptions) {
o.Regex = regex o.Regex = regex
} }
} }
func WithMatchOne(matchOne bool) ScreenFilterOption { func WithMatchOne(matchOne bool) ActionOption {
return func(o *ScreenFilterOptions) { return func(o *ActionOptions) {
o.MatchOne = matchOne o.MatchOne = matchOne
} }
} }
func WithIndex(index int) ScreenFilterOption { func WithIndex(index int) ActionOption {
return func(o *ScreenFilterOptions) { return func(o *ActionOptions) {
o.Index = index o.Index = index
} }
} }
+60
View File
@@ -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"
)
+2 -65
View File
@@ -1,8 +1,6 @@
package uixt package types
import ( import "fmt"
"fmt"
)
type DeviceStatus struct { type DeviceStatus struct {
Message string `json:"message"` 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. // DeviceButton A physical button on an iOS device.
type DeviceButton string type DeviceButton string
@@ -249,8 +188,6 @@ type Rotation struct {
Z int `json:"z"` Z int `json:"z"`
} }
type Condition func(wd IDriver) (bool, error)
type Direction string type Direction string
const ( const (
+10
View File
@@ -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
}
+23 -27
View File
@@ -235,7 +235,7 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) {
// release UI driver session // release UI driver session
defer func() { defer func() {
for _, client := range caseRunner.uixtDrivers { 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, TestCase: testcase,
hrpRunner: r, hrpRunner: r,
parser: newParser(), parser: newParser(),
uixtDrivers: make(map[string]*uixt.DriverExt), uixtDrivers: make(map[string]uixt.IDriverExt),
} }
config := testcase.Config.Get() config := testcase.Config.Get()
@@ -336,7 +336,7 @@ type CaseRunner struct {
parametersIterator *ParametersIterator parametersIterator *ParametersIterator
// UI automation clients for iOS and Android, key is udid/serial // 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 { 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") return nil, errors.Wrap(err, "init android device failed")
} }
if err := device.Setup(); err != nil { 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 { 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[androidDevice.SerialNumber] = driverExt
}
r.uixtDrivers[device.SerialNumber] = driver
} }
// parse iOS devices config // parse iOS devices config
for _, iosDevice := range parsedConfig.IOS { for _, iosDevice := range parsedConfig.IOS {
@@ -446,21 +444,20 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
return nil, errors.Wrap(code.InvalidCaseError, return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse ios config failed: %v", err)) fmt.Sprintf("parse ios config failed: %v", err))
} }
device, err := uixt.NewIOSDevice(iosDevice.Options()...) device, err := uixt.NewIOSDevice(iosDevice.Options()...)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "init ios device failed") return nil, errors.Wrap(err, "init ios device failed")
} }
if err := device.Setup(); err != nil { 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 { 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[iosDevice.UDID] = driverExt
}
r.uixtDrivers[device.UDID] = driver
} }
// parse harmony devices config // parse harmony devices config
for _, harmonyDevice := range parsedConfig.Harmony { for _, harmonyDevice := range parsedConfig.Harmony {
@@ -469,21 +466,20 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
return nil, errors.Wrap(code.InvalidCaseError, return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse harmony config failed: %v", err)) fmt.Sprintf("parse harmony config failed: %v", err))
} }
device, err := uixt.NewHarmonyDevice(harmonyDevice.Options()...) device, err := uixt.NewHarmonyDevice(harmonyDevice.Options()...)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "init harmony device failed") return nil, errors.Wrap(err, "init harmony device failed")
} }
if err := device.Setup(); err != nil { 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 { 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[harmonyDevice.ConnectKey] = driverExt
}
r.uixtDrivers[device.ConnectKey] = driver
} }
return parsedConfig, nil return parsedConfig, nil
@@ -525,7 +521,7 @@ func (r *CaseRunner) parseDeviceConfig(device interface{}, configVariables map[s
return nil 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 { for key, driver := range r.uixtDrivers {
// return the driver with the same serial // return the driver with the same serial
if key == serial { if key == serial {
@@ -604,8 +600,8 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
"uuid": uuid, "uuid": uuid,
} }
if client.Device.LogEnabled() { if client.GetDriver().GetDevice().LogEnabled() {
log, err1 := client.Driver.StopCaptureLog() log, err1 := client.GetDriver().StopCaptureLog()
if err1 != nil { if err1 != nil {
if err == nil { if err == nil {
err = errors.Wrap(err1, "stop capture log failed") err = errors.Wrap(err1, "stop capture log failed")
+2 -4
View File
@@ -13,7 +13,7 @@ import (
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "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 { func handleDeviceContext() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
@@ -54,9 +54,7 @@ func handleDeviceContext() gin.HandlerFunc {
} }
device.Setup() device.Setup()
driver, err := device.NewDriver( driver, err := device.NewDriver()
option.WithDriverImageService(true),
option.WithDriverResultFolder(true))
if err != nil { if err != nil {
log.Error().Err(err).Str("platform", platform).Str("serial", serial). log.Error().Err(err).Str("platform", platform).Str("serial", serial).
Msg("failed to init driver") Msg("failed to init driver")
+4 -2
View File
@@ -1,6 +1,8 @@
package server package server
import "github.com/httprunner/httprunner/v5/pkg/uixt/option" import (
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
type HttpResponse struct { type HttpResponse struct {
Code int `json:"code"` Code int `json:"code"`
@@ -31,7 +33,7 @@ type InputRequest struct {
} }
type ScreenRequest struct { type ScreenRequest struct {
Options *option.ActionOptions `json:"options,omitempty"` Options *option.ScreenOptions `json:"options,omitempty"`
} }
type KeycodeRequest struct { type KeycodeRequest struct {
+3 -3
View File
@@ -684,7 +684,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err
}, },
StartTime: startTime.Unix(), StartTime: startTime.Unix(),
} }
if app, err1 := uiDriver.Driver.GetForegroundApp(); err1 == nil { if app, err1 := uiDriver.GetDriver().GetForegroundApp(); err1 == nil {
attachments["foreground_app"] = app.AppBaseInfo attachments["foreground_app"] = app.AppBaseInfo
} else { } else {
log.Warn().Err(err1).Msg("save foreground app failed, ignore") 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 // save attachments
session := uiDriver.Driver.GetSession() session := uiDriver.GetDriver().GetSession()
for key, value := range session.GetData(true) { for key, value := range session.GetData(true) {
attachments[key] = value attachments[key] = value
} }
@@ -780,7 +780,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err
return stepResult, nil 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 { for _, iValidator := range iValidators {
validator, ok := iValidator.(Validator) validator, ok := iValidator.(Validator)
if !ok { if !ok {