refactor: XTDriver

This commit is contained in:
lilong.129
2025-02-11 11:48:36 +08:00
parent 3cb2eac442
commit c097461987
29 changed files with 194 additions and 174 deletions

View File

@@ -41,13 +41,13 @@ var installCmd = &cobra.Command{
fmt.Println(err)
return err
}
driverExt, err := device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
fmt.Println(err)
return err
}
err = driverExt.GetDriver().GetDevice().Install(args[0],
err = driver.GetDevice().Install(args[0],
option.WithReinstall(replace),
option.WithDowngrade(downgrade),
option.WithGrantPermission(grant),

View File

@@ -28,46 +28,48 @@ func init() {
fmt.Printf("=== start running cases, serial=%s, runTimes=%d ===\n", serial, runTimes)
}
func launchAppDriver(pkgName string) (driver uixt.IDriverExt, err error) {
func launchAppDriver(pkgName string) (driverExt *uixt.XTDriver, err error) {
device, _ := uixt.NewAndroidDevice(option.WithSerialNumber(serial))
driver, err = device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
return nil, err
}
_, err = driver.GetDriver().AppTerminate(pkgName)
_, err = driver.AppTerminate(pkgName)
if err != nil {
return nil, err
}
err = driver.GetDriver().Homescreen()
err = driver.Homescreen()
if err != nil {
return nil, err
}
err = driver.GetDriver().AppLaunch(pkgName)
err = driver.AppLaunch(pkgName)
if err != nil {
return nil, err
}
time.Sleep(15 * time.Second)
driverExt = uixt.NewXTDriver(driver)
// 处理弹窗
err = driver.ClosePopupsHandler()
err = driverExt.ClosePopupsHandler()
if err != nil {
return nil, err
}
// 进入推荐页
err = driver.TapByOCR("推荐", option.WithScope(0, 0, 1, 0.3))
err = driverExt.TapByOCR("推荐", option.WithScope(0, 0, 1, 0.3))
if err != nil {
return nil, err
}
return driver, nil
return driverExt, nil
}
func watchVideo(driver uixt.IDriverExt) (err error) {
func watchVideo(driver *uixt.XTDriver) (err error) {
time.Sleep(3 * time.Second)
err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2)
if err != nil {

View File

@@ -7,6 +7,7 @@ import (
"time"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
@@ -28,9 +29,9 @@ func init() {
fmt.Printf("=== start running cases, serial=%s, runTimes=%d ===\n", serial, runTimes)
}
func launchAppDriver(pkgName string) (driver uixt.IDriverExt, err error) {
func launchAppDriver(pkgName string) (driverExt *uixt.XTDriver, err error) {
device, _ := uixt.NewIOSDevice(option.WithUDID(serial))
driver, err = device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
return nil, err
}
@@ -52,22 +53,25 @@ func launchAppDriver(pkgName string) (driver uixt.IDriverExt, err error) {
time.Sleep(15 * time.Second)
driverExt = uixt.NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
// 处理弹窗
err = driver.ClosePopupsHandler()
err = driverExt.ClosePopupsHandler()
if err != nil {
return nil, err
}
// 进入推荐页
err = driver.TapByOCR("推荐", option.WithScope(0, 0, 1, 0.3))
err = driverExt.TapByOCR("推荐", option.WithScope(0, 0, 1, 0.3))
if err != nil {
return nil, err
}
return driver, nil
return driverExt, nil
}
func watchVideo(driver uixt.IDriverExt) (err error) {
func watchVideo(driver *uixt.XTDriver) (err error) {
time.Sleep(3 * time.Second)
err = driver.SwipeRelative(0.7, 0.7, 0.7, 0.2)
if err != nil {

View File

@@ -39,12 +39,9 @@ var rootCmd = &cobra.Command{
return errors.New("android or ios app bundldID is required")
}
driverExt, err := uixt.NewDriverExt(driver,
driverExt := uixt.NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM),
)
if err != nil {
return err
}
wc := NewWorldCupLive(driverExt, matchName, bundleID, duration, interval)

View File

@@ -37,7 +37,7 @@ func convertTimeToSeconds(timeStr string) (int, error) {
return seconds, nil
}
func initIOSDriver(uuid string) uixt.IDriverExt {
func initIOSDriver(uuid string) *uixt.XTDriver {
device, err := uixt.NewIOSDevice(
option.WithUDID(uuid),
option.WithWDAPort(8700), option.WithWDAMjpegPort(8800),
@@ -48,16 +48,18 @@ func initIOSDriver(uuid string) uixt.IDriverExt {
log.Fatal().Err(err).Msg("failed to init ios device")
}
driver, _ := device.NewDriver()
return driver
driverExt := uixt.NewXTDriver(driver)
return driverExt
}
func initAndroidDriver(uuid string) uixt.IDriverExt {
func initAndroidDriver(uuid string) *uixt.XTDriver {
device, err := uixt.NewAndroidDevice(option.WithSerialNumber(uuid))
if err != nil {
log.Fatal().Err(err).Msg("failed to init android device")
}
driver, _ := device.NewDriver()
return driver
driverExt := uixt.NewXTDriver(driver)
return driverExt
}
type timeLog struct {
@@ -68,7 +70,7 @@ type timeLog struct {
}
type WorldCupLive struct {
driver uixt.IDriverExt
driver *uixt.XTDriver
file *os.File
resultDir string
MatchName string `json:"matchName"`
@@ -81,7 +83,7 @@ type WorldCupLive struct {
PerfFile string `json:"perf"`
}
func NewWorldCupLive(driver uixt.IDriverExt, matchName, bundleID string, duration, interval int) *WorldCupLive {
func NewWorldCupLive(driver *uixt.XTDriver, matchName, bundleID string, duration, interval int) *WorldCupLive {
if matchName == "" {
matchName = "unknown-match"
}

View File

@@ -1 +1 @@
v5.0.0+2502102239
v5.0.0+2502111148

View File

@@ -24,7 +24,6 @@ import (
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/gadb"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
)
@@ -149,8 +148,7 @@ func (dev *AndroidDevice) LogEnabled() bool {
return dev.Options.LogOn
}
func (dev *AndroidDevice) NewDriver() (driverExt IDriverExt, err error) {
var driver IDriver
func (dev *AndroidDevice) NewDriver() (driver IDriver, err error) {
if dev.Options.UIA2 || dev.Options.LogOn {
driver, err = NewUIA2Driver(dev)
} else {
@@ -166,16 +164,11 @@ func (dev *AndroidDevice) NewDriver() (driverExt IDriverExt, err error) {
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 {
if err := driver.Setup(); err != nil {
return nil, err
}
return driverExt, nil
return driver, nil
}
func (dev *AndroidDevice) StartPerf() error {

View File

@@ -9,19 +9,22 @@ import (
"time"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
)
var driverExt IDriverExt
var driverExt *XTDriver
func setupAndroidAdbDriver(t *testing.T) {
device, err := NewAndroidDevice()
checkErr(t, err)
device.Options.UIA2 = false
device.Options.LogOn = false
driverExt, err = device.NewDriver()
driver, err = device.NewDriver()
checkErr(t, err)
driverExt = NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
}
func setupAndroidUIA2Driver(t *testing.T) {
@@ -29,8 +32,10 @@ func setupAndroidUIA2Driver(t *testing.T) {
checkErr(t, err)
device.Options.UIA2 = true
device.Options.LogOn = false
driverExt, err = device.NewDriver()
driver, err = device.NewDriver()
checkErr(t, err)
driverExt = NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
}
func TestAndroidDevice_GetPackageInfo(t *testing.T) {
@@ -252,12 +257,12 @@ func TestDriver_AppLaunch(t *testing.T) {
t.Fatal(err)
}
err = driver.GetDriver().AppLaunch("com.android.settings")
err = driver.AppLaunch("com.android.settings")
if err != nil {
t.Fatal(err)
}
raw, err := driver.GetDriver().Screenshot()
raw, err := driver.Screenshot()
if err != nil {
t.Fatal(err)
}
@@ -305,19 +310,19 @@ func TestDriver_KeepAlive(t *testing.T) {
t.Fatal(err)
}
err = driver.GetDriver().AppLaunch("com.android.settings")
err = driver.AppLaunch("com.android.settings")
if err != nil {
t.Fatal(err)
}
_, err = driver.GetDriver().Screenshot()
_, err = driver.Screenshot()
if err != nil {
t.Fatal(err)
}
time.Sleep(60 * time.Second)
_, err = driver.GetDriver().Screenshot()
_, err = driver.Screenshot()
if err != nil {
t.Fatal(err)
}
@@ -330,7 +335,7 @@ func TestDriver_AppTerminate(t *testing.T) {
t.Fatal(err)
}
_, err = driver.GetDriver().AppTerminate("tv.danmaku.bili")
_, err = driver.AppTerminate("tv.danmaku.bili")
if err != nil {
t.Fatal(err)
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
@@ -22,10 +23,13 @@ func TestIOSDemo(t *testing.T) {
t.Fatal(err)
}
driverExt, err := device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
t.Fatal(err)
}
driverExt := uixt.NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM),
)
// release session
defer func() {

View File

@@ -147,6 +147,37 @@ type IDriver interface {
TearDown() error
}
func NewXTDriver(driver IDriver, opts ...ai.AIServiceOption) *XTDriver {
services := ai.NewAIService(opts...)
driverExt := &XTDriver{
Driver: driver,
CVService: services.ICVService,
LLMService: services.ILLMService,
}
return driverExt
}
func NewDriverExt(driver IDriver, opts ...ai.AIServiceOption) (*XTDriver, error) {
services := ai.NewAIService(opts...)
driverExt := &XTDriver{
Driver: driver,
CVService: services.ICVService,
LLMService: services.ILLMService,
}
// create results directory
// TODO: move to setup
if err := builtin.EnsureFolderExists(config.ResultsPath); err != nil {
return nil, errors.Wrap(err, "create results directory failed")
}
if err := builtin.EnsureFolderExists(config.ScreenShotsPath); err != nil {
return nil, errors.Wrap(err, "create screenshots directory failed")
}
return driverExt, nil
}
var _ IDriverExt = (*XTDriver)(nil)
// XTDriver = IDriver + AI
type IDriverExt interface {
GetDriver() IDriver // get original driver
@@ -176,35 +207,17 @@ type IDriverExt interface {
DoValidation(check, assert, expected string, message ...string) (err error)
}
func NewDriverExt(driver IDriver, opts ...ai.AIServiceOption) (IDriverExt, error) {
services := ai.NewAIService(opts...)
driverExt := &DriverExt{
Driver: driver,
CVService: services.ICVService,
LLMService: services.ILLMService,
}
// create results directory
// TODO: move to setup
if err := builtin.EnsureFolderExists(config.ResultsPath); err != nil {
return nil, errors.Wrap(err, "create results directory failed")
}
if err := builtin.EnsureFolderExists(config.ScreenShotsPath); err != nil {
return nil, errors.Wrap(err, "create screenshots directory failed")
}
return driverExt, nil
}
type DriverExt struct {
type XTDriver struct {
Driver IDriver
CVService ai.ICVService // OCR/CV
LLMService ai.ILLMService // LLM
}
func (dExt *DriverExt) GetDriver() IDriver {
func (dExt *XTDriver) GetDriver() IDriver {
return dExt.Driver
}
func (dExt *DriverExt) Setup() error {
func (dExt *XTDriver) Setup() error {
// unlock device screen
err := dExt.Driver.Unlock()
if err != nil {
@@ -215,7 +228,7 @@ func (dExt *DriverExt) Setup() error {
return nil
}
func (dExt *DriverExt) assertOCR(text, assert string) error {
func (dExt *XTDriver) assertOCR(text, assert string) error {
var opts []option.ActionOption
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text)))
@@ -248,7 +261,7 @@ func (dExt *DriverExt) assertOCR(text, assert string) error {
return nil
}
func (dExt *DriverExt) assertForegroundApp(appName, assert string) (err error) {
func (dExt *XTDriver) assertForegroundApp(appName, assert string) (err error) {
err = dExt.Driver.AssertForegroundApp(appName)
switch assert {
case AssertionEqual:
@@ -265,7 +278,7 @@ func (dExt *DriverExt) assertForegroundApp(appName, assert string) (err error) {
return nil
}
func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...string) (err error) {
func (dExt *XTDriver) DoValidation(check, assert, expected string, message ...string) (err error) {
switch check {
case SelectorOCR:
err = dExt.assertOCR(expected, assert)

View File

@@ -103,7 +103,7 @@ type TapTextAction struct {
Options []option.ActionOption
}
func (dExt *DriverExt) ParseActionOptions(opts ...option.ActionOption) []option.ActionOption {
func (dExt *XTDriver) ParseActionOptions(opts ...option.ActionOption) []option.ActionOption {
actionOptions := option.NewActionOptions(opts...)
// convert relative scope to absolute scope
@@ -116,7 +116,7 @@ func (dExt *DriverExt) ParseActionOptions(opts ...option.ActionOption) []option.
return actionOptions.Options()
}
func (dExt *DriverExt) GenAbsScope(x1, y1, x2, y2 float64) option.AbsScope {
func (dExt *XTDriver) GenAbsScope(x1, y1, x2, y2 float64) option.AbsScope {
// convert relative scope to absolute scope
windowSize, _ := dExt.Driver.WindowSize()
absX1 := int(x1 * float64(windowSize.Width))
@@ -126,7 +126,7 @@ func (dExt *DriverExt) GenAbsScope(x1, y1, x2, y2 float64) option.AbsScope {
return option.AbsScope{absX1, absY1, absX2, absY2}
}
func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
actionStartTime := time.Now()
defer func() {
var logger *zerolog.Event

View File

@@ -8,7 +8,7 @@ import (
"github.com/pkg/errors"
)
func (dExt *DriverExt) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) (err error) {
windowSize, err := dExt.Driver.WindowSize()
if err != nil {
return errors.Wrap(code.DeviceGetInfoError, err.Error())

View File

@@ -6,7 +6,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
)
func (dExt *DriverExt) Input(text string) (err error) {
func (dExt *XTDriver) Input(text string) (err error) {
err = dExt.Driver.Input(text)
if err != nil {
return errors.Wrap(code.MobileUIInputError, err.Error())

View File

@@ -18,7 +18,7 @@ type InstallResult struct {
ErrorMsg string `json:"errorMsg"`
}
func (dExt *DriverExt) InstallByUrl(url string, opts ...option.InstallOption) error {
func (dExt *XTDriver) InstallByUrl(url string, opts ...option.InstallOption) error {
// 获取当前目录
cwd, err := os.Getwd()
if err != nil {
@@ -41,7 +41,7 @@ func (dExt *DriverExt) InstallByUrl(url string, opts ...option.InstallOption) er
return nil
}
func (dExt *DriverExt) Install(filePath string, opts ...option.InstallOption) error {
func (dExt *XTDriver) Install(filePath string, opts ...option.InstallOption) error {
if _, ok := dExt.Driver.GetDevice().(*AndroidDevice); ok {
stopChan := make(chan struct{})
go func() {
@@ -90,7 +90,7 @@ func (dExt *DriverExt) Install(filePath string, opts ...option.InstallOption) er
return dExt.Driver.GetDevice().Install(filePath, opts...)
}
func (dExt *DriverExt) Uninstall(packageName string, opts ...option.ActionOption) error {
func (dExt *XTDriver) Uninstall(packageName string, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
err := dExt.Driver.GetDevice().Uninstall(packageName)
if err != nil {

View File

@@ -43,7 +43,7 @@ func findTextPopup(screenTexts ai.OCRTexts) (closePoint *ai.OCRText) {
return
}
func (dExt *DriverExt) handleTextPopup(screenTexts ai.OCRTexts) error {
func (dExt *XTDriver) handleTextPopup(screenTexts ai.OCRTexts) error {
closePoint := findTextPopup(screenTexts)
if closePoint == nil {
// no popup found
@@ -60,7 +60,7 @@ func (dExt *DriverExt) handleTextPopup(screenTexts ai.OCRTexts) error {
return nil
}
func (dExt *DriverExt) AutoPopupHandler() error {
func (dExt *XTDriver) AutoPopupHandler() error {
// TODO: check popup by activity type
// check popup by screenshot
@@ -98,7 +98,7 @@ func (p *PopupInfo) ClosePoint() *ai.PointF {
return &closePoint
}
func (dExt *DriverExt) CheckPopup() (popup *PopupInfo, err error) {
func (dExt *XTDriver) CheckPopup() (popup *PopupInfo, err error) {
screenResult, err := dExt.GetScreenResult(
option.WithScreenShotUpload(true),
option.WithScreenShotClosePopups(true), // get popup area and close area
@@ -122,7 +122,7 @@ func (dExt *DriverExt) CheckPopup() (popup *PopupInfo, err error) {
return popup, nil
}
func (dExt *DriverExt) ClosePopupsHandler() (err error) {
func (dExt *XTDriver) ClosePopupsHandler() (err error) {
log.Info().Msg("try to find and close popups")
popup, err := dExt.CheckPopup()

View File

@@ -46,7 +46,7 @@ func (s *ScreenResult) FilterTextsByScope(x1, y1, x2, y2 float64) ai.OCRTexts {
}
// GetScreenResult takes a screenshot, returns the image recognition result
func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) {
func (dExt *XTDriver) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) {
screenshotOptions := option.NewActionOptions(opts...)
var fileName string
@@ -129,7 +129,7 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
return screenResult, nil
}
func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error) {
func (dExt *XTDriver) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error) {
options := option.NewActionOptions(opts...)
if options.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName("get_screen_texts"))
@@ -142,7 +142,7 @@ func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.
return screenResult.Texts, nil
}
func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOption) (point ai.PointF, err error) {
func (dExt *XTDriver) FindUIRectInUIKit(search string, opts ...option.ActionOption) (point ai.PointF, err error) {
// find text using OCR
if !builtin.IsPathExists(search) {
return dExt.FindScreenText(search, opts...)
@@ -152,7 +152,7 @@ func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOpt
return
}
func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) (point ai.PointF, err error) {
func (dExt *XTDriver) FindScreenText(text string, opts ...option.ActionOption) (point ai.PointF, err error) {
options := option.NewActionOptions(opts...)
if options.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text)))
@@ -174,7 +174,7 @@ func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption)
return
}
func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.PointF, err error) {
func (dExt *XTDriver) FindUIResult(opts ...option.ActionOption) (point ai.PointF, err error) {
options := option.NewActionOptions(opts...)
if options.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(
@@ -199,7 +199,7 @@ func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.Point
}
// GetScreenShot takes screenshot and saves image file to $CWD/screenshots/ folder
func (dExt *DriverExt) GetScreenShot(fileName string) (raw *bytes.Buffer, path string, err error) {
func (dExt *XTDriver) GetScreenShot(fileName string) (raw *bytes.Buffer, path string, err error) {
if raw, err = dExt.Driver.Screenshot(); err != nil {
log.Error().Err(err).Msg("capture screenshot data failed")
return nil, "", errors.Wrap(code.DeviceScreenShotError, err.Error())

View File

@@ -19,7 +19,7 @@ func assertRelative(p float64) bool {
}
// SwipeRelative swipe from relative position [fromX, fromY] to relative position [toX, toY]
func (dExt *DriverExt) SwipeRelative(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
func (dExt *XTDriver) SwipeRelative(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
if !assertRelative(fromX) || !assertRelative(fromY) ||
!assertRelative(toX) || !assertRelative(toY) {
return errors.Wrap(code.InvalidCaseError,
@@ -45,25 +45,25 @@ func (dExt *DriverExt) SwipeRelative(fromX, fromY, toX, toY float64, opts ...opt
return nil
}
func (dExt *DriverExt) SwipeUp(opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) SwipeUp(opts ...option.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.5, 0.1, opts...)
}
func (dExt *DriverExt) SwipeDown(opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) SwipeDown(opts ...option.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.5, 0.9, opts...)
}
func (dExt *DriverExt) SwipeLeft(opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) SwipeLeft(opts ...option.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.1, 0.5, opts...)
}
func (dExt *DriverExt) SwipeRight(opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) SwipeRight(opts ...option.ActionOption) (err error) {
return dExt.SwipeRelative(0.5, 0.5, 0.9, 0.5, opts...)
}
type Action func(driver *DriverExt) error
type Action func(driver *XTDriver) error
func (dExt *DriverExt) LoopUntil(findAction, findCondition, foundAction Action, opts ...option.ActionOption) error {
func (dExt *XTDriver) LoopUntil(findAction, findCondition, foundAction Action, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
maxRetryTimes := actionOptions.MaxRetryTimes
interval := actionOptions.Interval
@@ -86,7 +86,7 @@ func (dExt *DriverExt) LoopUntil(findAction, findCondition, foundAction Action,
fmt.Sprintf("loop %d times, match find condition failed", maxRetryTimes))
}
func prepareSwipeAction(dExt *DriverExt, params interface{}, opts ...option.ActionOption) func(d *DriverExt) error {
func prepareSwipeAction(dExt *XTDriver, params interface{}, opts ...option.ActionOption) func(d *XTDriver) error {
actionOptions := option.NewActionOptions(opts...)
var swipeDirection interface{}
@@ -103,7 +103,7 @@ func prepareSwipeAction(dExt *DriverExt, params interface{}, opts ...option.Acti
actionOptions.Steps = 10
}
return func(d *DriverExt) error {
return func(d *XTDriver) error {
defer func() {
// wait for swipe action to completed and content to load completely
time.Sleep(time.Duration(1000*actionOptions.Interval) * time.Millisecond)
@@ -138,7 +138,7 @@ func prepareSwipeAction(dExt *DriverExt, params interface{}, opts ...option.Acti
}
}
func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOption) error {
func (dExt *XTDriver) swipeToTapTexts(texts []string, opts ...option.ActionOption) error {
if len(texts) == 0 {
return errors.New("no text to tap")
}
@@ -148,7 +148,7 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOpti
actionOptions.Identifier = ""
optionsWithoutIdentifier := actionOptions.Options()
var point ai.PointF
findTexts := func(d *DriverExt) error {
findTexts := func(d *XTDriver) error {
var err error
screenResult, err := d.GetScreenResult(
option.WithScreenShotOCR(true),
@@ -172,7 +172,7 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOpti
point = points[0].Center() // FIXME
return nil
}
foundTextAction := func(d *DriverExt) error {
foundTextAction := func(d *XTDriver) error {
// tap text
return d.TapAbsXY(point.X, point.Y, opts...)
}
@@ -181,7 +181,7 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOpti
return dExt.LoopUntil(findAction, findTexts, foundTextAction, optionsWithoutIdentifier...)
}
func (dExt *DriverExt) SwipeToTapApp(appName string, opts ...option.ActionOption) error {
func (dExt *XTDriver) SwipeToTapApp(appName string, opts ...option.ActionOption) error {
// go to home screen
if err := dExt.Driver.Homescreen(); err != nil {
return errors.Wrap(err, "go to home screen failed")

View File

@@ -11,13 +11,12 @@ import (
func TestAndroidSwipeAction(t *testing.T) {
setupAndroidAdbDriver(t)
dExt := driverExt.(*DriverExt)
swipeAction := prepareSwipeAction(dExt, "up", option.WithDirection("down"))
err := swipeAction(dExt)
swipeAction := prepareSwipeAction(driverExt, "up", option.WithDirection("down"))
err := swipeAction(driverExt)
checkErr(t, err)
swipeAction = prepareSwipeAction(dExt, "up", option.WithCustomDirection(0.5, 0.5, 0.5, 0.9))
err = swipeAction(dExt)
swipeAction = prepareSwipeAction(driverExt, "up", option.WithCustomDirection(0.5, 0.5, 0.5, 0.9))
err = swipeAction(driverExt)
checkErr(t, err)
}
@@ -34,7 +33,6 @@ func TestAndroidSwipeToTapTexts(t *testing.T) {
err := driverExt.GetDriver().AppLaunch("com.ss.android.ugc.aweme")
checkErr(t, err)
dExt := driverExt.(*DriverExt)
err = dExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, option.WithDirection("up"))
err = driverExt.swipeToTapTexts([]string{"点击进入直播间", "直播中"}, option.WithDirection("up"))
checkErr(t, err)
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
func (dExt *DriverExt) TapAbsXY(x, y float64, opts ...option.ActionOption) error {
func (dExt *XTDriver) TapAbsXY(x, y float64, opts ...option.ActionOption) error {
// tap on absolute coordinate [x, y]
err := dExt.Driver.Tap(x, y, opts...)
if err != nil {
@@ -18,7 +18,7 @@ func (dExt *DriverExt) TapAbsXY(x, y float64, opts ...option.ActionOption) error
return nil
}
func (dExt *DriverExt) TapXY(x, y float64, opts ...option.ActionOption) error {
func (dExt *XTDriver) TapXY(x, y float64, opts ...option.ActionOption) error {
// tap on [x, y] percent of window size
if x > 1 || y > 1 {
return fmt.Errorf("x, y percentage should be <= 1, got x=%v, y=%v", x, y)
@@ -33,7 +33,7 @@ func (dExt *DriverExt) TapXY(x, y float64, opts ...option.ActionOption) error {
return dExt.TapAbsXY(x, y, opts...)
}
func (dExt *DriverExt) TapByOCR(ocrText string, opts ...option.ActionOption) error {
func (dExt *XTDriver) TapByOCR(ocrText string, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("tap_by_ocr_%s", ocrText)))
@@ -50,7 +50,7 @@ func (dExt *DriverExt) TapByOCR(ocrText string, opts ...option.ActionOption) err
return dExt.TapAbsXY(point.X, point.Y, opts...)
}
func (dExt *DriverExt) TapByUIDetection(opts ...option.ActionOption) error {
func (dExt *XTDriver) TapByUIDetection(opts ...option.ActionOption) error {
options := option.NewActionOptions(opts...)
point, err := dExt.FindUIResult(opts...)
@@ -64,11 +64,11 @@ func (dExt *DriverExt) TapByUIDetection(opts ...option.ActionOption) error {
return dExt.TapAbsXY(point.X, point.Y, opts...)
}
func (dExt *DriverExt) Tap(param string, opts ...option.ActionOption) error {
func (dExt *XTDriver) Tap(param string, opts ...option.ActionOption) error {
return dExt.TapOffset(param, 0, 0, opts...)
}
func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) TapOffset(param string, xOffset, yOffset float64, opts ...option.ActionOption) (err error) {
options := option.NewActionOptions(opts...)
point, err := dExt.FindUIRectInUIKit(param, opts...)
@@ -82,7 +82,7 @@ func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, opts ..
return dExt.TapAbsXY(point.X+xOffset, point.Y+yOffset, opts...)
}
func (dExt *DriverExt) DoubleTapXY(x, y float64, opts ...option.ActionOption) error {
func (dExt *XTDriver) DoubleTapXY(x, y float64, opts ...option.ActionOption) error {
// double tap on coordinate: [x, y] should be relative
if x > 1 || y > 1 {
return fmt.Errorf("x, y percentage should be < 1, got x=%v, y=%v", x, y)
@@ -101,11 +101,11 @@ func (dExt *DriverExt) DoubleTapXY(x, y float64, opts ...option.ActionOption) er
return nil
}
func (dExt *DriverExt) DoubleTap(param string, opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) DoubleTap(param string, opts ...option.ActionOption) (err error) {
return dExt.DoubleTapOffset(param, 0, 0, opts...)
}
func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64, opts ...option.ActionOption) (err error) {
func (dExt *XTDriver) DoubleTapOffset(param string, xOffset, yOffset float64, opts ...option.ActionOption) (err error) {
point, err := dExt.FindUIRectInUIKit(param)
if err != nil {
return err

View File

@@ -4,35 +4,34 @@ package uixt
import (
"testing"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
)
var iosDevice *IOSDevice
var (
iosDevice *IOSDevice
iosDriverExt *XTDriver
)
func init() {
iosDevice, _ = NewIOSDevice()
driver, _ := iosDevice.NewDriver()
iosDriverExt = NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
}
func TestDriverExt_TapXY(t *testing.T) {
driverExt, err := iosDevice.NewDriver()
checkErr(t, err)
err = driverExt.TapXY(0.4, 0.5)
err := iosDriverExt.TapXY(0.4, 0.5)
checkErr(t, err)
}
func TestDriverExt_TapAbsXY(t *testing.T) {
driverExt, err := iosDevice.NewDriver()
checkErr(t, err)
err = driverExt.TapAbsXY(100, 300)
err := iosDriverExt.TapAbsXY(100, 300)
checkErr(t, err)
}
func TestDriverExt_TapWithOCR(t *testing.T) {
driverExt, err := iosDevice.NewDriver()
checkErr(t, err)
// 需要点击文字上方的图标
err = driverExt.TapOffset("抖音", 0, -20)
err := iosDriverExt.TapOffset("抖音", 0, -20)
checkErr(t, err)
}

View File

@@ -15,7 +15,7 @@ func TestNewDriverExt(t *testing.T) {
t.Fatal(err)
}
driverExt, _ := NewDriverExt(driver,
driverExt := NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
texts, _ := driverExt.GetScreenTexts()

View File

@@ -6,7 +6,6 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
)
@@ -94,21 +93,15 @@ func (dev *HarmonyDevice) GetPackageInfo(packageName string) (types.AppInfo, err
return types.AppInfo{}, nil
}
func (dev *HarmonyDevice) NewDriver() (IDriverExt, error) {
func (dev *HarmonyDevice) NewDriver() (IDriver, 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 {
if err := driver.Setup(); err != nil {
return nil, err
}
return driverExt, nil
return driver, nil
}

View File

@@ -5,9 +5,14 @@ package uixt
import (
"fmt"
"testing"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
)
var hdcDriver *HDCDriver
var (
hdcDriver *HDCDriver
hdcDriverExt *XTDriver
)
func setupHarmonyDevice(t *testing.T) {
device, err := NewHarmonyDevice()
@@ -18,6 +23,8 @@ func setupHarmonyDevice(t *testing.T) {
if err != nil {
t.Fatal(err)
}
hdcDriverExt = NewXTDriver(hdcDriver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
}
func TestWindowSize(t *testing.T) {
@@ -31,7 +38,7 @@ func TestWindowSize(t *testing.T) {
func TestHarmonyTap(t *testing.T) {
setupHarmonyDevice(t)
err := hdcDriver.TapAbsXY(200, 2000)
err := hdcDriverExt.TapAbsXY(200, 2000)
if err != nil {
t.Fatal(err)
}
@@ -39,7 +46,7 @@ func TestHarmonyTap(t *testing.T) {
func TestHarmonySwipe(t *testing.T) {
setupHarmonyDevice(t)
err := hdcDriver.SwipeLeft()
err := hdcDriverExt.SwipeLeft()
if err != nil {
t.Fatal(err)
}

View File

@@ -28,7 +28,6 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
)
@@ -203,8 +202,8 @@ func (dev *IOSDevice) getAppInfo(packageName string) (appInfo types.AppInfo, err
return types.AppInfo{}, fmt.Errorf("not found App by bundle id: %s", packageName)
}
func (dev *IOSDevice) NewDriver() (driverExt IDriverExt, err error) {
driver, err := NewWDADriver(dev)
func (dev *IOSDevice) NewDriver() (driver IDriver, err error) {
driver, err = NewWDADriver(dev)
if err != nil {
return nil, errors.Wrap(err, "failed to init WDA driver")
}
@@ -230,18 +229,11 @@ func (dev *IOSDevice) NewDriver() (driverExt IDriverExt, err error) {
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 {
if err := driver.Setup(); err != nil {
return nil, err
}
return driverExt, nil
return driver, nil
}
func (dev *IOSDevice) Install(appPath string, opts ...option.InstallOption) (err error) {

View File

@@ -10,6 +10,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
)
@@ -17,7 +18,7 @@ import (
var (
bundleId = "com.apple.Preferences"
driver IDriver
iOSDriverExt IDriverExt
iOSDriverExt *XTDriver
)
func setup(t *testing.T) {
@@ -30,10 +31,11 @@ func setup(t *testing.T) {
}
capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
iOSDriverExt, err = device.NewDriver()
driver, err = device.NewDriver()
if err != nil {
t.Fatal(err)
}
iOSDriverExt = NewXTDriver(driver, ai.WithCVService(ai.CVServiceTypeVEDEM))
}
func TestViaUSB(t *testing.T) {

View File

@@ -22,7 +22,7 @@ type timeLog struct {
}
type EndToEndDelay struct {
driver *DriverExt
driver *XTDriver
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
Interval int `json:"interval"` // seconds
@@ -30,7 +30,7 @@ type EndToEndDelay struct {
Timelines []timeLog `json:"timelines"`
}
func CollectEndToEndDelay(dExt *DriverExt, opts ...option.ActionOption) {
func CollectEndToEndDelay(dExt *XTDriver, opts ...option.ActionOption) {
dataOptions := option.NewActionOptions(opts...)
startTime := time.Now()

View File

@@ -27,6 +27,7 @@ import (
"github.com/httprunner/httprunner/v5/internal/sdk"
"github.com/httprunner/httprunner/v5/internal/version"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
)
// Run starts to run testcase with default configs.
@@ -281,7 +282,7 @@ func (r *HRPRunner) NewCaseRunner(testcase TestCase) (*CaseRunner, error) {
TestCase: testcase,
hrpRunner: r,
parser: newParser(),
uixtDrivers: make(map[string]uixt.IDriverExt),
uixtDrivers: make(map[string]*uixt.XTDriver),
}
config := testcase.Config.Get()
@@ -336,7 +337,7 @@ type CaseRunner struct {
parametersIterator *ParametersIterator
// UI automation clients for iOS and Android, key is udid/serial
uixtDrivers map[string]uixt.IDriverExt
uixtDrivers map[string]*uixt.XTDriver
}
func (r *CaseRunner) GetParametersIterator() *ParametersIterator {
@@ -430,11 +431,12 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
if err := device.Setup(); err != nil {
return nil, errors.Wrap(err, "setup android device failed")
}
driverExt, err := device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init android driver failed")
}
driverExt := uixt.NewXTDriver(driver, ai.WithCVService(ai.CVServiceTypeVEDEM))
r.uixtDrivers[androidDevice.Options.SerialNumber] = driverExt
}
// parse iOS devices config
@@ -452,11 +454,12 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
if err := device.Setup(); err != nil {
return nil, errors.Wrap(err, "setup ios device failed")
}
driverExt, err := device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init ios driver failed")
}
driverExt := uixt.NewXTDriver(driver, ai.WithCVService(ai.CVServiceTypeVEDEM))
r.uixtDrivers[iosDevice.Options.UDID] = driverExt
}
// parse harmony devices config
@@ -474,11 +477,12 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
if err := device.Setup(); err != nil {
return nil, errors.Wrap(err, "setup harmony device failed")
}
driverExt, err := device.NewDriver()
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init harmony driver failed")
}
driverExt := uixt.NewXTDriver(driver, ai.WithCVService(ai.CVServiceTypeVEDEM))
r.uixtDrivers[harmonyDevice.Options.ConnectKey] = driverExt
}
@@ -521,7 +525,7 @@ func (r *CaseRunner) parseDeviceConfig(device interface{}, configVariables map[s
return nil
}
func (r *CaseRunner) GetUIXTDriver(serial string) (driver uixt.IDriverExt, err error) {
func (r *CaseRunner) GetUIXTDriver(serial string) (driver *uixt.XTDriver, err error) {
for key, driver := range r.uixtDrivers {
// return the driver with the same serial
if key == serial {

View File

@@ -10,10 +10,11 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
var uiClients = make(map[string]uixt.IDriverExt) // UI automation clients for iOS and Android, key is udid/serial
var uiClients = make(map[string]*uixt.XTDriver) // UI automation clients for iOS and Android, key is udid/serial
func (r *Router) HandleDeviceContext() gin.HandlerFunc {
return func(c *gin.Context) {
@@ -66,9 +67,13 @@ func (r *Router) HandleDeviceContext() gin.HandlerFunc {
c.Abort()
return
}
c.Set("driver", driver)
driverExt := uixt.NewXTDriver(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
c.Set("driver", driverExt)
// cache driver
uiClients[serial] = driver
uiClients[serial] = driverExt
default:
c.JSON(http.StatusBadRequest, HttpResponse{
Code: code.GetErrorCode(code.InvalidParamError),
@@ -81,13 +86,13 @@ func (r *Router) HandleDeviceContext() gin.HandlerFunc {
}
}
func GetContextDriver(c *gin.Context) (uixt.IDriverExt, error) {
func GetContextDriver(c *gin.Context) (*uixt.XTDriver, error) {
driverObj, exists := c.Get("driver")
if !exists {
handlerInitDeviceDriverFailedContext(c)
return nil, fmt.Errorf("driver not found")
}
dExt := driverObj.(*uixt.DriverExt)
dExt := driverObj.(*uixt.XTDriver)
return dExt, nil
}

View File

@@ -780,7 +780,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err
return stepResult, nil
}
func validateUI(ud uixt.IDriverExt, iValidators []interface{}) (validateResults []*ValidationResult, err error) {
func validateUI(ud *uixt.XTDriver, iValidators []interface{}) (validateResults []*ValidationResult, err error) {
for _, iValidator := range iValidators {
validator, ok := iValidator.(Validator)
if !ok {