refactor: split ai related logic to pkg/ai

This commit is contained in:
lilong.129
2025-02-08 10:38:48 +08:00
parent 06d7a7e721
commit 5e45eb7836
32 changed files with 798 additions and 487 deletions

View File

@@ -1,303 +0,0 @@
package uixt
import (
"bytes"
"fmt"
"image"
"math"
"regexp"
"github.com/pkg/errors"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
type IImageService interface {
// GetImage returns image result including ocr texts, uploaded image url, etc
GetImage(imageBuf *bytes.Buffer, opts ...option.ActionOption) (imageResult *ImageResult, err error)
}
type ImageResult struct {
URL string `json:"url,omitempty"` // image uploaded url
OCRResult OCRResults `json:"ocrResult,omitempty"` // OCR texts
// NoLive非直播间
// Shop电商
// LifeService生活服务
// Show秀场
// Game游戏
// People多人
// PKPK
// Media媒体
// Chat语音
// Event赛事
LiveType string `json:"liveType,omitempty"` // 直播间类型
LivePopularity int64 `json:"livePopularity,omitempty"` // 直播间热度
UIResult UIResultMap `json:"uiResult,omitempty"` // 图标检测
ClosePopupsResult *ClosePopupsResult `json:"closeResult,omitempty"` // 弹窗按钮检测
}
type OCRResult struct {
Text string `json:"text"`
Points []PointF `json:"points"`
}
type OCRResults []OCRResult
func (o OCRResults) ToOCRTexts() (ocrTexts OCRTexts) {
for _, ocrResult := range o {
rect := image.Rectangle{
// ocrResult.Points 顺序:左上 -> 右上 -> 右下 -> 左下
Min: image.Point{
X: int(ocrResult.Points[0].X),
Y: int(ocrResult.Points[0].Y),
},
Max: image.Point{
X: int(ocrResult.Points[2].X),
Y: int(ocrResult.Points[2].Y),
},
}
rectStr := fmt.Sprintf("%d,%d,%d,%d",
rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y)
ocrText := OCRText{
Text: ocrResult.Text,
Rect: rect,
RectStr: rectStr,
}
ocrTexts = append(ocrTexts, ocrText)
}
return
}
type OCRText struct {
Text string `json:"text"`
RectStr string `json:"rect"`
Rect image.Rectangle `json:"-"`
}
func (t OCRText) Size() Size {
return Size{
Width: t.Rect.Dx(),
Height: t.Rect.Dy(),
}
}
func (t OCRText) Center() PointF {
return getRectangleCenterPoint(t.Rect)
}
func getRectangleCenterPoint(rect image.Rectangle) (point PointF) {
x, y := float64(rect.Min.X), float64(rect.Min.Y)
width, height := float64(rect.Dx()), float64(rect.Dy())
point = PointF{
X: x + width*0.5,
Y: y + height*0.5,
}
return point
}
type OCRTexts []OCRText
func (t OCRTexts) texts() (texts []string) {
for _, text := range t {
texts = append(texts, text.Text)
}
return texts
}
func (t OCRTexts) FilterScope(scope option.AbsScope) (results OCRTexts) {
for _, ocrText := range t {
rect := ocrText.Rect
// check if text in scope
if len(scope) == 4 {
if rect.Min.X < scope[0] ||
rect.Min.Y < scope[1] ||
rect.Max.X > scope[2] ||
rect.Max.Y > scope[3] {
// not in scope
continue
}
}
results = append(results, ocrText)
}
return
}
// FindText returns matched text with options
// Notice: filter scope should be specified with WithAbsScope
func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCRText, err error) {
actionOptions := option.NewActionOptions(opts...)
var results []OCRText
for _, ocrText := range t.FilterScope(actionOptions.AbsScope) {
if actionOptions.Regex {
// regex on, check if match regex
if !regexp.MustCompile(text).MatchString(ocrText.Text) {
continue
}
} else {
// regex off, check if match exactly
if ocrText.Text != text {
continue
}
}
results = append(results, ocrText)
// return the first one matched exactly when index not specified
if ocrText.Text == text && actionOptions.Index == 0 {
return ocrText, nil
}
}
if len(results) == 0 {
return OCRText{}, errors.Wrap(code.CVResultNotFoundError,
fmt.Sprintf("text %s not found in %v", text, t.texts()))
}
// get index
idx := actionOptions.Index
if idx < 0 {
idx = len(results) + idx
}
// index out of range
if idx >= len(results) || idx < 0 {
return OCRText{}, errors.Wrap(code.CVResultNotFoundError,
fmt.Sprintf("text %s found %d, index %d out of range", text, len(results), idx))
}
return results[idx], nil
}
func (t OCRTexts) FindTexts(texts []string, opts ...option.ActionOption) (results OCRTexts, err error) {
actionOptions := option.NewActionOptions(opts...)
for _, text := range texts {
ocrText, err := t.FindText(text, opts...)
if err != nil {
continue
}
results = append(results, ocrText)
// found one, skip searching and return
if actionOptions.MatchOne {
return results, nil
}
}
if len(results) == len(texts) {
return results, nil
}
return nil, errors.Wrap(code.CVResultNotFoundError,
fmt.Sprintf("texts %s not found in %v", texts, t.texts()))
}
type UIResultMap map[string]UIResults
// FilterUIResults filters ui icons, the former the uiTypes, the higher the priority
func (u UIResultMap) FilterUIResults(uiTypes []string) (uiResults UIResults, err error) {
var ok bool
for _, uiType := range uiTypes {
uiResults, ok = u[uiType]
if ok && len(uiResults) != 0 {
return
}
}
err = errors.Wrap(code.CVResultNotFoundError, fmt.Sprintf("UI types %v not detected", uiTypes))
return
}
type UIResult struct {
Box
}
type Box struct {
Point PointF `json:"point"`
Width float64 `json:"width"`
Height float64 `json:"height"`
}
func (box Box) IsEmpty() bool {
return builtin.IsZeroFloat64(box.Width) && builtin.IsZeroFloat64(box.Height)
}
func (box Box) IsIdentical(box2 Box) bool {
// set the coordinate precision to 1 pixel
return box.Point.IsIdentical(box2.Point) &&
builtin.IsZeroFloat64(math.Abs(box.Width-box2.Width)) &&
builtin.IsZeroFloat64(math.Abs(box.Height-box2.Height))
}
func (box Box) Center() PointF {
return PointF{
X: box.Point.X + box.Width*0.5,
Y: box.Point.Y + box.Height*0.5,
}
}
type UIResults []UIResult
func (u UIResults) FilterScope(scope option.AbsScope) (results UIResults) {
for _, uiResult := range u {
rect := image.Rectangle{
Min: image.Point{
X: int(uiResult.Point.X),
Y: int(uiResult.Point.Y),
},
Max: image.Point{
X: int(uiResult.Point.X + uiResult.Width),
Y: int(uiResult.Point.Y + uiResult.Height),
},
}
// check if ui result in scope
if len(scope) == 4 {
if rect.Min.X < scope[0] ||
rect.Min.Y < scope[1] ||
rect.Max.X > scope[2] ||
rect.Max.Y > scope[3] {
// not in scope
continue
}
}
results = append(results, uiResult)
}
return
}
func (u UIResults) GetUIResult(opts ...option.ActionOption) (UIResult, error) {
actionOptions := option.NewActionOptions(opts...)
uiResults := u.FilterScope(actionOptions.AbsScope)
if len(uiResults) == 0 {
return UIResult{}, errors.Wrap(code.CVResultNotFoundError,
"ui types not found in scope")
}
// get index
idx := actionOptions.Index
if idx < 0 {
idx = len(uiResults) + idx
}
// index out of range
if idx >= len(uiResults) || idx < 0 {
return UIResult{}, errors.Wrap(code.CVResultNotFoundError,
fmt.Sprintf("ui types index %d out of range", idx))
}
return uiResults[idx], nil
}
// ClosePopupsResult represents the result of recognized popup to close
type ClosePopupsResult struct {
Type string `json:"type"`
PopupArea Box `json:"popupArea"`
CloseArea Box `json:"closeArea"`
Text string `json:"text"`
}
func (c ClosePopupsResult) IsEmpty() bool {
return c.PopupArea.IsEmpty() && c.CloseArea.IsEmpty()
}

View File

@@ -1,249 +0,0 @@
package uixt
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"time"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
var client = &http.Client{
Timeout: time.Second * 10,
}
type APIResponseImage struct {
Code int `json:"code"`
Message string `json:"message"`
Result ImageResult `json:"result"`
}
func newVEDEMImageService() (*veDEMImageService, error) {
if err := checkEnv(); err != nil {
return nil, err
}
return &veDEMImageService{}, nil
}
// veDEMImageService implements IImageService interface
// actions:
//
// ocr - get ocr texts
// upload - get image uploaded url
// liveType - get live type
// popup - get popup windows
// close - get close popup
// ui - get ui position by type(s)
type veDEMImageService struct{}
func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, opts ...option.ActionOption) (imageResult *ImageResult, err error) {
actionOptions := option.NewActionOptions(opts...)
screenshotActions := actionOptions.ScreenshotActions()
if len(screenshotActions) == 0 {
// skip
return nil, nil
}
start := time.Now()
defer func() {
elapsed := time.Since(start).Milliseconds()
var logger *zerolog.Event
if err != nil {
logger = log.Error().Err(err)
} else {
logger = log.Debug()
if imageResult.URL != "" {
logger = logger.Str("url", imageResult.URL)
}
if imageResult.UIResult != nil {
logger = logger.Interface("uiResult", imageResult.UIResult)
}
if imageResult.ClosePopupsResult != nil {
if imageResult.ClosePopupsResult.IsEmpty() {
// set nil to reduce unnecessary summary info
imageResult.ClosePopupsResult = nil
} else {
logger = logger.Interface("closePopupsResult", imageResult.ClosePopupsResult)
}
}
}
logger = logger.Int64("elapsed(ms)", elapsed)
logger.Msg("get image data by veDEM")
}()
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
for _, action := range screenshotActions {
bodyWriter.WriteField("actions", action)
}
for _, uiType := range actionOptions.ScreenShotWithUITypes {
bodyWriter.WriteField("uiTypes", uiType)
}
// 使用高精度集群
bodyWriter.WriteField("ocrCluster", "highPrecision")
if actionOptions.ScreenShotWithOCRCluster != "" {
bodyWriter.WriteField("ocrCluster", actionOptions.ScreenShotWithOCRCluster)
}
if actionOptions.Timeout > 0 {
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", actionOptions.Timeout))
} else {
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", 10))
}
formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png")
if err != nil {
err = errors.Wrap(code.CVRequestError,
fmt.Sprintf("create form file error: %v", err))
return
}
size, err := formWriter.Write(imageBuf.Bytes())
if err != nil {
err = errors.Wrap(code.CVRequestError,
fmt.Sprintf("write form error: %v", err))
return
}
err = bodyWriter.Close()
if err != nil {
err = errors.Wrap(code.CVRequestError,
fmt.Sprintf("close body writer error: %v", err))
return
}
var req *http.Request
var resp *http.Response
// retry 3 times
for i := 1; i <= 3; i++ {
copiedBodyBuf := &bytes.Buffer{}
if _, err := copiedBodyBuf.Write(bodyBuf.Bytes()); err != nil {
log.Error().Err(err).Msg("copy screenshot buffer failed")
continue
}
req, err = http.NewRequest("POST", os.Getenv("VEDEM_IMAGE_URL"), copiedBodyBuf)
if err != nil {
err = errors.Wrap(code.CVRequestError,
fmt.Sprintf("construct request error: %v", err))
return
}
// ppe env
// req.Header.Add("x-tt-env", "ppe_vedem_algorithm")
// req.Header.Add("x-use-ppe", "1")
signToken := "UNSIGNED-PAYLOAD"
token := builtin.Sign("auth-v2", os.Getenv("VEDEM_IMAGE_AK"), os.Getenv("VEDEM_IMAGE_SK"), []byte(signToken))
req.Header.Add("Agw-Auth", token)
req.Header.Add("Agw-Auth-Content", signToken)
req.Header.Add("Content-Type", bodyWriter.FormDataContentType())
start := time.Now()
resp, err = client.Do(req)
elapsed := time.Since(start)
if err != nil {
log.Error().Err(err).
Int("imageBufSize", size).
Msgf("request veDEM OCR service error, retry %d", i)
continue
}
logID := getLogID(resp.Header)
statusCode := resp.StatusCode
if statusCode != http.StatusOK {
log.Error().
Str("X-TT-LOGID", logID).
Int("imageBufSize", size).
Int("statusCode", statusCode).
Msgf("request veDEM OCR service failed, retry %d", i)
time.Sleep(1 * time.Second)
continue
}
log.Debug().
Str("X-TT-LOGID", logID).
Int("image_bytes", size).
Int64("elapsed(ms)", elapsed.Milliseconds()).
Msg("request OCR service success")
break
}
if resp == nil {
err = code.CVServiceConnectionError
return
}
defer resp.Body.Close()
results, err := io.ReadAll(resp.Body)
if err != nil {
err = errors.Wrap(code.CVResponseError,
fmt.Sprintf("read response body error: %v", err))
return
}
if resp.StatusCode != http.StatusOK {
err = errors.Wrap(code.CVResponseError,
fmt.Sprintf("unexpected response status code: %d, results: %v",
resp.StatusCode, string(results)))
return
}
var imageResponse APIResponseImage
err = json.Unmarshal(results, &imageResponse)
if err != nil {
err = errors.Wrap(code.CVResponseError,
fmt.Sprintf("json unmarshal veDEM image response body error, response=%s", string(results)))
return
}
if imageResponse.Code != 0 {
err = errors.Wrap(code.CVResponseError,
fmt.Sprintf("unexpected response data code: %d, message: %s",
imageResponse.Code, imageResponse.Message))
return
}
imageResult = &imageResponse.Result
return imageResult, nil
}
func checkEnv() error {
vedemImageURL := os.Getenv("VEDEM_IMAGE_URL")
if vedemImageURL == "" {
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_URL missed")
}
log.Info().Str("VEDEM_IMAGE_URL", vedemImageURL).Msg("get env")
if os.Getenv("VEDEM_IMAGE_AK") == "" {
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_AK missed")
}
if os.Getenv("VEDEM_IMAGE_SK") == "" {
return errors.Wrap(code.CVEnvMissedError, "VEDEM_IMAGE_SK missed")
}
return nil
}
func getLogID(header http.Header) string {
if len(header) == 0 {
return ""
}
logID, ok := header["X-Tt-Logid"]
if !ok || len(logID) == 0 {
return ""
}
return logID[0]
}

View File

@@ -1,80 +0,0 @@
//go:build localtest
package uixt
import (
"bytes"
"fmt"
"os"
"testing"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
func checkOCR(buff *bytes.Buffer) error {
service, err := newVEDEMImageService()
if err != nil {
return err
}
imageResult, err := service.GetImage(buff)
if err != nil {
return err
}
fmt.Println(fmt.Sprintf("imageResult: %v", imageResult))
return nil
}
func TestOCRWithScreenshot(t *testing.T) {
setupAndroidAdbDriver(t)
raw, err := driverExt.Driver.Screenshot()
if err != nil {
t.Fatal(err)
}
if err := checkOCR(raw); err != nil {
t.Fatal(err)
}
}
func TestOCRWithLocalFile(t *testing.T) {
imagePath := "/Users/debugtalk/Downloads/s1.png"
file, err := os.ReadFile(imagePath)
if err != nil {
t.Fatal(err)
}
buf := new(bytes.Buffer)
buf.Read(file)
if err := checkOCR(buf); err != nil {
t.Fatal(err)
}
}
func TestTapUIWithScreenshot(t *testing.T) {
serialNumber := os.Getenv("SERIAL_NUMBER")
device, _ := NewAndroidDevice(option.WithSerialNumber(serialNumber))
driver, err := device.NewDriver()
if err != nil {
t.Fatal(err)
}
err = driver.TapByUIDetection(
option.WithScreenShotUITypes("dyhouse", "shoppingbag"))
if err != nil {
t.Fatal(err)
}
}
func TestDriverExtOCR(t *testing.T) {
driverExt, err := iosDevice.NewDriver()
checkErr(t, err)
point, err := driverExt.FindScreenText("抖音")
checkErr(t, err)
t.Logf("point.X: %v, point.Y: %v", point.X, point.Y)
driverExt.Driver.Tap(point.X, point.Y-20)
}

View File

@@ -152,7 +152,7 @@ func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *Dri
if dev.UIA2 || dev.LogOn {
driver, err = NewUIA2Driver(dev)
} else if dev.STUB {
driver, err = NewStubDriver(dev)
driver, err = NewStubAndroidDriver(dev)
} else {
driver, err = NewADBDriver(dev)
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/utf7"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
@@ -39,6 +40,7 @@ func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) {
type ADBDriver struct {
*AndroidDevice
*Session
// DriverExt
}
func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, err error) {
@@ -89,6 +91,10 @@ func (ad *ADBDriver) Status() (deviceStatus DeviceStatus, err error) {
return
}
func (ad *ADBDriver) GetDevice() IDevice {
return ad.AndroidDevice
}
func (ad *ADBDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
err = errDriverNotImplemented
return
@@ -104,7 +110,7 @@ func (ad *ADBDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
return
}
func (ad *ADBDriver) getWindowSize() (size Size, err error) {
func (ad *ADBDriver) getWindowSize() (size ai.Size, err error) {
// adb shell wm size
output, err := ad.runShellCommand("wm", "size")
if err != nil {
@@ -130,14 +136,14 @@ func (ad *ADBDriver) getWindowSize() (size Size, err error) {
ss := strings.Split(resolution, "x")
width, _ := strconv.Atoi(ss[0])
height, _ := strconv.Atoi(ss[1])
return Size{Width: width, Height: height}, nil
return ai.Size{Width: width, Height: height}, nil
}
}
err = errors.New("physical window size not found by adb")
return
}
func (ad *ADBDriver) WindowSize() (size Size, err error) {
func (ad *ADBDriver) WindowSize() (size ai.Size, err error) {
if !ad.windowSize.IsNil() {
// use cached window size
return ad.windowSize, nil
@@ -163,7 +169,7 @@ func (ad *ADBDriver) WindowSize() (size Size, err error) {
return size, nil
}
func (ad *ADBDriver) Screen() (screen Screen, err error) {
func (ad *ADBDriver) Screen() (screen ai.Screen, err error) {
err = errDriverNotImplemented
return
}
@@ -172,21 +178,6 @@ func (ad *ADBDriver) Scale() (scale float64, err error) {
return 1, nil
}
func (ad *ADBDriver) GetTimestamp() (timestamp int64, err error) {
// adb shell date +%s
output, err := ad.runShellCommand("date", "+%s")
if err != nil {
return 0, errors.Wrap(err, "failed to get timestamp by adb")
}
timestamp, err = strconv.ParseInt(strings.TrimSpace(output), 10, 64)
if err != nil {
return 0, errors.Wrap(err, "convert timestamp failed")
}
return timestamp, nil
}
// PressBack simulates a short press on the BACK button.
func (ad *ADBDriver) PressBack(opts ...option.ActionOption) (err error) {
// adb shell input keyevent 4
@@ -1088,6 +1079,10 @@ func (ad *ADBDriver) RecordScreen(folderPath string, duration time.Duration) (vi
return filepath.Abs(fileName)
}
func (ad *ADBDriver) Setup() error {
return nil
}
func (ad *ADBDriver) TearDown() error {
return nil
}

View File

@@ -22,7 +22,7 @@ const (
DouyinServerPort = 32316
)
func NewStubDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) {
func NewStubAndroidDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) {
socketLocalPort, err := device.Forward(StubSocketName)
if err != nil {
return nil, errors.Wrap(code.DeviceConnectionError,

View File

@@ -14,7 +14,7 @@ func setupStubDriver(t *testing.T) {
device, err := NewAndroidDevice()
checkErr(t, err)
device.STUB = true
androidStubDriver, err = NewStubDriver(device)
androidStubDriver, err = NewStubAndroidDriver(device)
checkErr(t, err)
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/utf7"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
@@ -182,7 +183,7 @@ func (ud *UIA2Driver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
return
}
func (ud *UIA2Driver) WindowSize() (size Size, err error) {
func (ud *UIA2Driver) WindowSize() (size ai.Size, err error) {
// register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size"))
if !ud.windowSize.IsNil() {
// use cached window size
@@ -191,11 +192,11 @@ func (ud *UIA2Driver) WindowSize() (size Size, err error) {
var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "window/:windowHandle/size"); err != nil {
return Size{}, errors.Wrap(err, "get window size failed by UIA2 request")
return ai.Size{}, errors.Wrap(err, "get window size failed by UIA2 request")
}
reply := new(struct{ Value struct{ Size } })
reply := new(struct{ Value struct{ ai.Size } })
if err = json.Unmarshal(rawResp, reply); err != nil {
return Size{}, errors.Wrap(err, "get window size failed by UIA2 response")
return ai.Size{}, errors.Wrap(err, "get window size failed by UIA2 response")
}
size = reply.Value.Size

View File

@@ -6,17 +6,16 @@ import (
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
type IDevice interface {
UUID() string // ios udid or android serial
Setup() error
Teardown() error
UUID() string // ios udid or android serial
LogEnabled() bool
// TODO: remove
NewDriver(...option.DriverOption) (driverExt *DriverExt, err error)
Install(appPath string, opts ...option.InstallOption) error
Uninstall(packageName string) error
GetPackageInfo(packageName string) (AppInfo, error)
// TODO: remove?
LogEnabled() bool
}

View File

@@ -16,9 +16,17 @@ import (
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
var (
_ IDriver = (*ADBDriver)(nil)
_ IDriver = (*UIA2Driver)(nil)
_ IDriver = (*WDADriver)(nil)
_ IDriver = (*HDCDriver)(nil)
)
// current implemeted driver: ADBDriver, UIA2Driver, WDADriver, HDCDriver
type IDriver interface {
// NewSession starts a new session and returns the DriverSession.
@@ -34,6 +42,7 @@ type IDriver interface {
Status() (DeviceStatus, error)
GetDevice() IDevice
DeviceInfo() (DeviceInfo, error)
// Location Returns device location data.
@@ -53,13 +62,10 @@ type IDriver interface {
// WindowSize Return the width and height in portrait mode.
// when getting the window size in wda/ui2/adb, if the device is in landscape mode,
// the width and height will be reversed.
WindowSize() (Size, error)
Screen() (Screen, error)
WindowSize() (ai.Size, error)
Screen() (ai.Screen, error)
Scale() (float64, error)
// GetTimestamp returns the timestamp of the mobile device
GetTimestamp() (timestamp int64, err error)
// Homescreen Forces the device under test to switch to the home screen
Homescreen() error
@@ -162,40 +168,66 @@ type IDriver interface {
GetDriverResults() []*DriverRequests
RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error)
Setup() error
TearDown() error
}
type IDriverExt interface {
GetDriver() IDriver // get original driver
GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error)
GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error)
// swipe
SwipeRelative(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error
SwipeUp(opts ...option.ActionOption) error
SwipeDown(opts ...option.ActionOption) error
SwipeLeft(opts ...option.ActionOption) error
SwipeRight(opts ...option.ActionOption) error
}
func NewDriverExt(driver IDriver, opts ...ai.AIServiceOption) (IDriverExt, error) {
services := ai.NewAIService(opts...)
driverExt := &DriverExt{
Driver: driver,
ImageService: services.ImageService,
}
// create results directory
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 {
Device IDevice
Driver IDriver
ImageService IImageService // used to extract image data
ImageService ai.IImageService // used to extract image data
}
func newDriverExt(device IDevice, driver IDriver, opts ...option.DriverOption) (dExt *DriverExt, err error) {
options := option.NewDriverOptions(opts...)
dExt = &DriverExt{
Device: device,
Driver: driver,
}
if options.WithImageService {
if dExt.ImageService, err = newVEDEMImageService(); err != nil {
if dExt.ImageService, err = ai.NewVEDEMImageService(); err != nil {
return nil, err
}
}
if options.WithResultFolder {
// create results directory
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 dExt, nil
}
func (dExt *DriverExt) GetDriver() IDriver {
return dExt.Driver
}
func (dExt *DriverExt) Setup() error {
// unlock device screen
err := dExt.Driver.Unlock()

View File

@@ -42,7 +42,7 @@ func (dExt *DriverExt) InstallByUrl(url string, opts ...option.InstallOption) er
}
func (dExt *DriverExt) Install(filePath string, opts ...option.InstallOption) error {
if _, ok := dExt.Device.(*AndroidDevice); ok {
if _, ok := dExt.Driver.GetDevice().(*AndroidDevice); ok {
stopChan := make(chan struct{})
go func() {
ticker := time.NewTicker(5 * time.Second)
@@ -87,12 +87,12 @@ func (dExt *DriverExt) Install(filePath string, opts ...option.InstallOption) er
}()
}
return dExt.Device.Install(filePath, opts...)
return dExt.Driver.GetDevice().Install(filePath, opts...)
}
func (dExt *DriverExt) Uninstall(packageName string, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
err := dExt.Device.Uninstall(packageName)
err := dExt.Driver.GetDevice().Uninstall(packageName)
if err != nil {
log.Warn().Err(err).Msg("failed to uninstall")
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
@@ -25,7 +26,7 @@ var popups = [][]string{
{"管理使用时间", ".*忽略.*"},
}
func findTextPopup(screenTexts OCRTexts) (closePoint *OCRText) {
func findTextPopup(screenTexts ai.OCRTexts) (closePoint *ai.OCRText) {
for _, popup := range popups {
if len(popup) != 2 {
continue
@@ -42,7 +43,7 @@ func findTextPopup(screenTexts OCRTexts) (closePoint *OCRText) {
return
}
func (dExt *DriverExt) handleTextPopup(screenTexts OCRTexts) error {
func (dExt *DriverExt) handleTextPopup(screenTexts ai.OCRTexts) error {
closePoint := findTextPopup(screenTexts)
if closePoint == nil {
// no popup found
@@ -76,13 +77,13 @@ func (dExt *DriverExt) AutoPopupHandler() error {
}
type PopupInfo struct {
*ClosePopupsResult
ClosePoints []PointF `json:"close_points,omitempty"` // CV 识别的所有关闭按钮(仅关闭按钮,可能存在多个)
PicName string `json:"pic_name"`
PicURL string `json:"pic_url"`
*ai.ClosePopupsResult
ClosePoints []ai.PointF `json:"close_points,omitempty"` // CV 识别的所有关闭按钮(仅关闭按钮,可能存在多个)
PicName string `json:"pic_name"`
PicURL string `json:"pic_url"`
}
func (p *PopupInfo) ClosePoint() *PointF {
func (p *PopupInfo) ClosePoint() *ai.PointF {
closeResult := p.ClosePopupsResult
if closeResult == nil {
return nil

View File

@@ -18,21 +18,22 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
type ScreenResult struct {
bufSource *bytes.Buffer // raw image buffer bytes
ImagePath string `json:"image_path"` // image file path
Resolution Size `json:"resolution"`
UploadedURL string `json:"uploaded_url"` // uploaded image url
Texts OCRTexts `json:"texts"` // dumped raw OCRTexts
Icons UIResultMap `json:"icons"` // CV 识别的图标
Tags []string `json:"tags"` // tags for image, e.g. ["feed", "ad", "live"]
Popup *PopupInfo `json:"popup,omitempty"`
bufSource *bytes.Buffer // raw image buffer bytes
ImagePath string `json:"image_path"` // image file path
Resolution ai.Size `json:"resolution"`
UploadedURL string `json:"uploaded_url"` // uploaded image url
Texts ai.OCRTexts `json:"texts"` // dumped raw OCRTexts
Icons ai.UIResultMap `json:"icons"` // CV 识别的图标
Tags []string `json:"tags"` // tags for image, e.g. ["feed", "ad", "live"]
Popup *PopupInfo `json:"popup,omitempty"`
}
func (s *ScreenResult) FilterTextsByScope(x1, y1, x2, y2 float64) OCRTexts {
func (s *ScreenResult) FilterTextsByScope(x1, y1, x2, y2 float64) ai.OCRTexts {
if x1 > 1 || y1 > 1 || x2 > 1 || y2 > 1 {
log.Warn().Msg("x1, y1, x2, y2 should be in percentage, skip filter scope")
return s.Texts
@@ -46,9 +47,6 @@ func (s *ScreenResult) FilterTextsByScope(x1, y1, x2, y2 float64) OCRTexts {
// GetScreenResult takes a screenshot, returns the image recognition result
func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) {
actionOptions := option.NewActionOptions(opts...)
if actionOptions.MaxRetryTimes == 0 {
actionOptions.MaxRetryTimes = 1
}
var fileName string
screenshotActions := actionOptions.ScreenshotActions()
@@ -61,9 +59,9 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
}
var bufSource *bytes.Buffer
var imageResult *ImageResult
var imageResult *ai.ImageResult
var imagePath string
var windowSize Size
var windowSize ai.Size
var lastErr error
// get screenshot info with retry
@@ -87,9 +85,9 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
Tags: nil,
Resolution: windowSize,
}
imageResult, err = dExt.ImageService.GetImage(bufSource, opts...)
imageResult, err = dExt.ImageService.GetImageFromBuffer(bufSource, opts...)
if err != nil {
log.Error().Err(err).Msg("GetImage from ImageService failed")
log.Error().Err(err).Msg("GetImageFromBuffer from ImageService failed")
lastErr = err
continue
}
@@ -130,7 +128,7 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
return screenResult, nil
}
func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts OCRTexts, err error) {
func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts ai.OCRTexts, err error) {
actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName("get_screen_texts"))
@@ -143,7 +141,7 @@ func (dExt *DriverExt) GetScreenTexts(opts ...option.ActionOption) (ocrTexts OCR
return screenResult.Texts, nil
}
func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOption) (point PointF, err error) {
func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOption) (point ai.PointF, err error) {
// find text using OCR
if !builtin.IsPathExists(search) {
return dExt.FindScreenText(search, opts...)
@@ -153,7 +151,7 @@ func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOpt
return
}
func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) (point PointF, err error) {
func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption) (point ai.PointF, err error) {
actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text)))
@@ -175,7 +173,7 @@ func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption)
return
}
func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point PointF, err error) {
func (dExt *DriverExt) FindUIResult(opts ...option.ActionOption) (point ai.PointF, err error) {
actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(

View File

@@ -12,6 +12,7 @@ import (
"time"
"github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
@@ -24,7 +25,7 @@ type Session struct {
// cache to avoid repeated query
scale float64
windowSize Size
windowSize ai.Size
// cache uia2/wda request and response
requests []*DriverRequests

View File

@@ -10,6 +10,7 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
@@ -146,7 +147,7 @@ func (dExt *DriverExt) swipeToTapTexts(texts []string, opts ...option.ActionOpti
actionOptions := option.NewActionOptions(opts...)
actionOptions.Identifier = ""
optionsWithoutIdentifier := actionOptions.Options()
var point PointF
var point ai.PointF
findTexts := func(d *DriverExt) error {
var err error
screenResult, err := d.GetScreenResult(

29
pkg/uixt/driver_test.go Normal file
View File

@@ -0,0 +1,29 @@
package uixt
import (
"testing"
"github.com/httprunner/httprunner/v5/pkg/ai"
)
func TestNewDriverExt(t *testing.T) {
device, _ := NewAndroidDevice()
var driver IDriver
var err error
if device.UIA2 || device.LogOn {
driver, err = NewUIA2Driver(device)
} else if device.STUB {
driver, err = NewStubAndroidDriver(device)
} else {
driver, err = NewADBDriver(device)
}
if err != nil {
t.Fatal(err)
}
driverExt, _ := NewDriverExt(driver,
ai.WithCVService(ai.CVServiceTypeVEDEM))
driverExt.GetDriver()
t.Log(driverExt)
}

View File

@@ -98,31 +98,6 @@ func (dev *HarmonyDevice) LogEnabled() bool {
return dev.LogOn
}
func (dev *HarmonyDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
driver, err := newHarmonyDriver(dev.Device)
if err != nil {
log.Error().Err(err).Msg("failed to new harmony driver")
return nil, err
}
driverExt, err = newDriverExt(dev, driver, opts...)
if err != nil {
return nil, err
}
return driverExt, nil
}
func (dev *HarmonyDevice) NewUSBDriver(opts ...option.DriverOption) (driver IDriver, err error) {
harmonyDriver, err := newHarmonyDriver(dev.Device)
if err != nil {
log.Error().Err(err).Msg("failed to new harmony driver")
return nil, err
}
return harmonyDriver, nil
}
func (dev *HarmonyDevice) Install(appPath string, opts ...option.InstallOption) error {
return nil
}

View File

@@ -10,12 +10,15 @@ import (
"code.byted.org/iesqa/ghdc"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
type hdcDriver struct {
type HDCDriver struct {
*HarmonyDevice
*Session
IDriver
*DriverExt
points []ExportPoint
uiDriver *ghdc.UIDriver
}
@@ -28,10 +31,10 @@ const (
POWER_STATUS_ON PowerStatus = "POWER_STATUS_ON"
)
func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) {
driver = new(hdcDriver)
driver.Device = device
uiDriver, err := ghdc.NewUIDriver(*device)
func NewHDCDriver(device *HarmonyDevice) (driver *HDCDriver, err error) {
driver = new(HDCDriver)
driver.HarmonyDevice = device
uiDriver, err := ghdc.NewUIDriver(*device.Device)
if err != nil {
log.Error().Err(err).Msg("failed to new harmony ui driver")
return nil, err
@@ -41,64 +44,64 @@ func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) {
return
}
func (hd *hdcDriver) NewSession(capabilities option.Capabilities) (Session, error) {
func (hd *HDCDriver) NewSession(capabilities option.Capabilities) (Session, error) {
hd.Reset()
hd.Unlock()
return Session{}, errDriverNotImplemented
}
func (hd *hdcDriver) DeleteSession() error {
func (hd *HDCDriver) DeleteSession() error {
return errDriverNotImplemented
}
func (hd *hdcDriver) GetSession() *Session {
func (hd *HDCDriver) GetSession() *Session {
return hd.Session
}
func (hd *hdcDriver) Status() (DeviceStatus, error) {
func (hd *HDCDriver) Status() (DeviceStatus, error) {
return DeviceStatus{}, errDriverNotImplemented
}
func (hd *hdcDriver) DeviceInfo() (DeviceInfo, error) {
func (hd *HDCDriver) GetDevice() IDevice {
return hd.HarmonyDevice
}
func (hd *HDCDriver) DeviceInfo() (DeviceInfo, error) {
return DeviceInfo{}, errDriverNotImplemented
}
func (hd *hdcDriver) Location() (Location, error) {
func (hd *HDCDriver) Location() (Location, error) {
return Location{}, errDriverNotImplemented
}
func (hd *hdcDriver) BatteryInfo() (BatteryInfo, error) {
func (hd *HDCDriver) BatteryInfo() (BatteryInfo, error) {
return BatteryInfo{}, errDriverNotImplemented
}
func (hd *hdcDriver) WindowSize() (size Size, err error) {
func (hd *HDCDriver) WindowSize() (size ai.Size, err error) {
display, err := hd.uiDriver.GetDisplaySize()
if err != nil {
log.Error().Err(err).Msg("failed to get window size")
return Size{}, err
return ai.Size{}, err
}
size.Width = display.Width
size.Height = display.Height
return size, err
}
func (hd *hdcDriver) Screen() (Screen, error) {
return Screen{}, errDriverNotImplemented
func (hd *HDCDriver) Screen() (ai.Screen, error) {
return ai.Screen{}, errDriverNotImplemented
}
func (hd *hdcDriver) Scale() (float64, error) {
func (hd *HDCDriver) Scale() (float64, error) {
return 1, nil
}
func (hd *hdcDriver) GetTimestamp() (timestamp int64, err error) {
return 0, errDriverNotImplemented
}
func (hd *hdcDriver) Homescreen() error {
func (hd *HDCDriver) Homescreen() error {
return hd.uiDriver.PressKey(ghdc.KEYCODE_HOME)
}
func (hd *hdcDriver) Unlock() (err error) {
func (hd *HDCDriver) Unlock() (err error) {
// Todo 检查是否锁屏 hdc shell hidumper -s RenderService -a screen
screenInfo, err := hd.RunShellCommand("hidumper", "-s", "RenderService", "-a", "screen")
if err != nil {
@@ -120,12 +123,12 @@ func (hd *hdcDriver) Unlock() (err error) {
return hd.Swipe(500, 1500, 500, 500)
}
func (hd *hdcDriver) AppLaunch(packageName string) error {
func (hd *HDCDriver) AppLaunch(packageName string) error {
// Todo
return errDriverNotImplemented
}
func (hd *hdcDriver) AppTerminate(packageName string) (bool, error) {
func (hd *HDCDriver) AppTerminate(packageName string) (bool, error) {
_, err := hd.RunShellCommand("aa", "force-stop", packageName)
if err != nil {
log.Error().Err(err).Msg("failed to terminal app")
@@ -134,29 +137,29 @@ func (hd *hdcDriver) AppTerminate(packageName string) (bool, error) {
return true, nil
}
func (hd *hdcDriver) GetForegroundApp() (app AppInfo, err error) {
func (hd *HDCDriver) GetForegroundApp() (app AppInfo, err error) {
// Todo
return AppInfo{}, errDriverNotImplemented
}
func (hd *hdcDriver) AssertForegroundApp(packageName string, activityType ...string) error {
func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...string) error {
// Todo
return nil
}
func (hd *hdcDriver) StartCamera() error {
func (hd *HDCDriver) StartCamera() error {
return errDriverNotImplemented
}
func (hd *hdcDriver) StopCamera() error {
func (hd *HDCDriver) StopCamera() error {
return errDriverNotImplemented
}
func (hd *hdcDriver) Orientation() (orientation Orientation, err error) {
func (hd *HDCDriver) Orientation() (orientation Orientation, err error) {
return OrientationPortrait, nil
}
func (hd *hdcDriver) Tap(x, y float64, opts ...option.ActionOption) error {
func (hd *HDCDriver) Tap(x, y float64, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
if len(actionOptions.Offset) == 2 {
@@ -173,20 +176,20 @@ func (hd *hdcDriver) Tap(x, y float64, opts ...option.ActionOption) error {
return hd.uiDriver.InjectGesture(ghdc.NewGesture().Start(ghdc.Point{X: int(x), Y: int(y)}).Pause(100))
}
func (hd *hdcDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error {
func (hd *HDCDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) (err error) {
func (hd *HDCDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) (err error) {
return errDriverNotImplemented
}
func (hd *hdcDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
func (hd *HDCDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
return errDriverNotImplemented
}
// Swipe works like Drag, but `pressForDuration` value is 0
func (hd *hdcDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
func (hd *HDCDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0])
@@ -210,51 +213,51 @@ func (hd *hdcDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Action
return hd.uiDriver.InjectGesture(ghdc.NewGesture().Start(ghdc.Point{X: int(fromX), Y: int(fromY)}).MoveTo(ghdc.Point{X: int(toX), Y: int(toY)}, duration))
}
func (hd *hdcDriver) SetPasteboard(contentType PasteboardType, content string) error {
func (hd *HDCDriver) SetPasteboard(contentType PasteboardType, content string) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) {
func (hd *HDCDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) {
return nil, errDriverNotImplemented
}
func (hd *hdcDriver) SetIme(ime string) error {
func (hd *HDCDriver) SetIme(ime string) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) SendKeys(text string, opts ...option.ActionOption) error {
func (hd *HDCDriver) SendKeys(text string, opts ...option.ActionOption) error {
return hd.uiDriver.InputText(text)
}
func (hd *hdcDriver) Input(text string, opts ...option.ActionOption) error {
func (hd *HDCDriver) Input(text string, opts ...option.ActionOption) error {
return hd.uiDriver.InputText(text)
}
func (hd *hdcDriver) Clear(packageName string) error {
func (hd *HDCDriver) Clear(packageName string) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) PressButton(devBtn DeviceButton) error {
func (hd *HDCDriver) PressButton(devBtn DeviceButton) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) PressBack(opts ...option.ActionOption) error {
func (hd *HDCDriver) PressBack(opts ...option.ActionOption) error {
return hd.uiDriver.PressBack()
}
func (hd *hdcDriver) Backspace(count int, opts ...option.ActionOption) (err error) {
func (hd *HDCDriver) Backspace(count int, opts ...option.ActionOption) (err error) {
return nil
}
func (hd *hdcDriver) PressKeyCode(keyCode KeyCode) (err error) {
func (hd *HDCDriver) PressKeyCode(keyCode KeyCode) (err error) {
return errDriverNotImplemented
}
func (hd *hdcDriver) PressHarmonyKeyCode(keyCode ghdc.KeyCode) (err error) {
func (hd *HDCDriver) PressHarmonyKeyCode(keyCode ghdc.KeyCode) (err error) {
return hd.uiDriver.PressKey(keyCode)
}
func (hd *hdcDriver) Screenshot() (*bytes.Buffer, error) {
func (hd *HDCDriver) Screenshot() (*bytes.Buffer, error) {
tempDir := os.TempDir()
screenshotPath := fmt.Sprintf("%s/screenshot_%d.png", tempDir, time.Now().Unix())
err := hd.uiDriver.Screenshot(screenshotPath)
@@ -274,74 +277,78 @@ func (hd *hdcDriver) Screenshot() (*bytes.Buffer, error) {
return bytes.NewBuffer(raw), nil
}
func (hd *hdcDriver) Source(srcOpt ...option.SourceOption) (string, error) {
func (hd *HDCDriver) Source(srcOpt ...option.SourceOption) (string, error) {
return "", nil
}
func (hd *hdcDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
func (hd *HDCDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
err = errDriverNotImplemented
return
}
func (hd *hdcDriver) LogoutNoneUI(packageName string) error {
func (hd *HDCDriver) LogoutNoneUI(packageName string) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) TapByText(text string, opts ...option.ActionOption) error {
func (hd *HDCDriver) TapByText(text string, opts ...option.ActionOption) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) TapByTexts(actions ...TapTextAction) error {
func (hd *HDCDriver) TapByTexts(actions ...TapTextAction) error {
return errDriverNotImplemented
}
func (hd *hdcDriver) AccessibleSource() (string, error) {
func (hd *HDCDriver) AccessibleSource() (string, error) {
return "", errDriverNotImplemented
}
func (hd *hdcDriver) HealthCheck() error {
func (hd *HDCDriver) HealthCheck() error {
return errDriverNotImplemented
}
func (hd *hdcDriver) GetAppiumSettings() (map[string]interface{}, error) {
func (hd *HDCDriver) GetAppiumSettings() (map[string]interface{}, error) {
return nil, errDriverNotImplemented
}
func (hd *hdcDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) {
func (hd *HDCDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) {
return nil, errDriverNotImplemented
}
func (hd *hdcDriver) IsHealthy() (bool, error) {
func (hd *HDCDriver) IsHealthy() (bool, error) {
return false, errDriverNotImplemented
}
func (hd *hdcDriver) StartCaptureLog(identifier ...string) (err error) {
func (hd *HDCDriver) StartCaptureLog(identifier ...string) (err error) {
return errDriverNotImplemented
}
func (hd *hdcDriver) StopCaptureLog() (result interface{}, err error) {
func (hd *HDCDriver) StopCaptureLog() (result interface{}, err error) {
// defer clear(hd.points)
return hd.points, nil
}
func (hd *hdcDriver) GetDriverResults() []*DriverRequests {
func (hd *HDCDriver) GetDriverResults() []*DriverRequests {
return nil
}
func (hd *hdcDriver) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) {
func (hd *HDCDriver) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) {
return "", nil
}
func (hd *hdcDriver) TearDown() error {
func (hd *HDCDriver) Setup() error {
return nil
}
func (hd *hdcDriver) Rotation() (rotation Rotation, err error) {
func (hd *HDCDriver) TearDown() error {
return nil
}
func (hd *HDCDriver) Rotation() (rotation Rotation, err error) {
err = errDriverNotImplemented
return
}
func (hd *hdcDriver) SetRotation(rotation Rotation) (err error) {
func (hd *HDCDriver) SetRotation(rotation Rotation) (err error) {
err = errDriverNotImplemented
return
}

View File

@@ -7,18 +7,14 @@ import (
"testing"
)
var harmonyDriverExt *DriverExt
var hdcDriver *HDCDriver
func setupHarmonyDevice(t *testing.T) {
device, err := NewHarmonyDevice()
if err != nil {
t.Fatal(err)
}
driver, err = device.NewUSBDriver()
if err != nil {
t.Fatal(err)
}
harmonyDriverExt, err = newDriverExt(device, driver)
hdcDriver, err = NewHDCDriver(device)
if err != nil {
t.Fatal(err)
}
@@ -35,7 +31,7 @@ func TestWindowSize(t *testing.T) {
func TestHarmonyTap(t *testing.T) {
setupHarmonyDevice(t)
err := harmonyDriverExt.TapAbsXY(200, 2000)
err := hdcDriver.TapAbsXY(200, 2000)
if err != nil {
t.Fatal(err)
}
@@ -43,7 +39,7 @@ func TestHarmonyTap(t *testing.T) {
func TestHarmonySwipe(t *testing.T) {
setupHarmonyDevice(t)
err := harmonyDriverExt.SwipeLeft()
err := hdcDriver.SwipeLeft()
if err != nil {
t.Fatal(err)
}
@@ -51,7 +47,7 @@ func TestHarmonySwipe(t *testing.T) {
func TestHarmonyInput(t *testing.T) {
setupHarmonyDevice(t)
err := harmonyDriverExt.Input("test")
err := hdcDriver.Input("test")
if err != nil {
t.Fatal(err)
}

View File

@@ -552,7 +552,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID
Int("localPort", localPort).Int("localMjpegPort", localMjpegPort).
Msg("init WDA HTTP driver")
wd := new(wdaDriver)
wd := new(WDADriver)
wd.IOSDevice = dev
wd.udid = dev.UDID
wd.client = &http.Client{

View File

@@ -9,11 +9,12 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
type stubIOSDriver struct {
*wdaDriver
*WDADriver
bightInsightPrefix string
serverPrefix string
@@ -38,7 +39,7 @@ func newStubIOSDriver(bightInsightAddr, serverAddr string, dev *IOSDevice, readT
}
func (s *stubIOSDriver) setUpWda() (err error) {
if s.wdaDriver == nil {
if s.WDADriver == nil {
capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
driver, err := s.device.NewHTTPDriver(capabilities)
@@ -46,7 +47,7 @@ func (s *stubIOSDriver) setUpWda() (err error) {
log.Error().Err(err).Msg("stub driver failed to init wda driver")
return err
}
s.wdaDriver = driver.(*wdaDriver)
s.WDADriver = driver.(*WDADriver)
}
return nil
}
@@ -57,7 +58,7 @@ func (s *stubIOSDriver) NewSession(capabilities option.Capabilities) (Session, e
if err != nil {
return Session{}, err
}
return s.wdaDriver.NewSession(capabilities)
return s.WDADriver.NewSession(capabilities)
}
// DeleteSession Kills application associated with that session and removes session
@@ -68,7 +69,7 @@ func (s *stubIOSDriver) DeleteSession() error {
if err != nil {
return err
}
return s.wdaDriver.DeleteSession()
return s.WDADriver.DeleteSession()
}
func (s *stubIOSDriver) Status() (DeviceStatus, error) {
@@ -76,7 +77,7 @@ func (s *stubIOSDriver) Status() (DeviceStatus, error) {
if err != nil {
return DeviceStatus{}, err
}
return s.wdaDriver.Status()
return s.WDADriver.Status()
}
func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) {
@@ -84,7 +85,7 @@ func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) {
if err != nil {
return DeviceInfo{}, err
}
return s.wdaDriver.DeviceInfo()
return s.WDADriver.DeviceInfo()
}
func (s *stubIOSDriver) Location() (Location, error) {
@@ -92,7 +93,7 @@ func (s *stubIOSDriver) Location() (Location, error) {
if err != nil {
return Location{}, err
}
return s.wdaDriver.Location()
return s.WDADriver.Location()
}
func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) {
@@ -100,26 +101,26 @@ func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) {
if err != nil {
return BatteryInfo{}, err
}
return s.wdaDriver.BatteryInfo()
return s.WDADriver.BatteryInfo()
}
// WindowSize Return the width and height in portrait mode.
// when getting the window size in wda/ui2/adb, if the device is in landscape mode,
// the width and height will be reversed.
func (s *stubIOSDriver) WindowSize() (Size, error) {
func (s *stubIOSDriver) WindowSize() (ai.Size, error) {
err := s.setUpWda()
if err != nil {
return Size{}, err
return ai.Size{}, err
}
return s.wdaDriver.WindowSize()
return s.WDADriver.WindowSize()
}
func (s *stubIOSDriver) Screen() (Screen, error) {
func (s *stubIOSDriver) Screen() (ai.Screen, error) {
err := s.setUpWda()
if err != nil {
return Screen{}, err
return ai.Screen{}, err
}
return s.wdaDriver.Screen()
return s.WDADriver.Screen()
}
func (s *stubIOSDriver) Scale() (float64, error) {
@@ -127,16 +128,7 @@ func (s *stubIOSDriver) Scale() (float64, error) {
if err != nil {
return 0, err
}
return s.wdaDriver.Scale()
}
// GetTimestamp returns the timestamp of the mobile device
func (s *stubIOSDriver) GetTimestamp() (timestamp int64, err error) {
err = s.setUpWda()
if err != nil {
return 0, err
}
return s.wdaDriver.GetTimestamp()
return s.WDADriver.Scale()
}
// Homescreen Forces the device under test to switch to the home screen
@@ -145,7 +137,7 @@ func (s *stubIOSDriver) Homescreen() error {
if err != nil {
return err
}
return s.wdaDriver.Homescreen()
return s.WDADriver.Homescreen()
}
func (s *stubIOSDriver) Unlock() (err error) {
@@ -153,7 +145,7 @@ func (s *stubIOSDriver) Unlock() (err error) {
if err != nil {
return err
}
return s.wdaDriver.Unlock()
return s.WDADriver.Unlock()
}
// AppLaunch Launch an application with given bundle identifier in scope of current session.
@@ -163,7 +155,7 @@ func (s *stubIOSDriver) AppLaunch(packageName string) error {
if err != nil {
return err
}
return s.wdaDriver.AppLaunch(packageName)
return s.WDADriver.AppLaunch(packageName)
}
// AppTerminate Terminate an application with the given package name.
@@ -173,7 +165,7 @@ func (s *stubIOSDriver) AppTerminate(packageName string) (bool, error) {
if err != nil {
return false, err
}
return s.wdaDriver.AppTerminate(packageName)
return s.WDADriver.AppTerminate(packageName)
}
// GetForegroundApp returns current foreground app package name and activity name
@@ -182,16 +174,7 @@ func (s *stubIOSDriver) GetForegroundApp() (app AppInfo, err error) {
if err != nil {
return AppInfo{}, err
}
return s.wdaDriver.GetForegroundApp()
}
// AssertForegroundApp returns nil if the given package and activity are in foreground
func (s *stubIOSDriver) AssertForegroundApp(packageName string, activityType ...string) error {
err := s.setUpWda()
if err != nil {
return err
}
return s.wdaDriver.AssertForegroundApp(packageName, activityType...)
return s.WDADriver.GetForegroundApp()
}
// StartCamera Starts a new camera for recording
@@ -200,7 +183,7 @@ func (s *stubIOSDriver) StartCamera() error {
if err != nil {
return err
}
return s.wdaDriver.StartCamera()
return s.WDADriver.StartCamera()
}
// StopCamera Stops the camera for recording
@@ -209,7 +192,7 @@ func (s *stubIOSDriver) StopCamera() error {
if err != nil {
return err
}
return s.wdaDriver.StopCamera()
return s.WDADriver.StopCamera()
}
func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) {
@@ -217,7 +200,7 @@ func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) {
if err != nil {
return OrientationPortrait, err
}
return s.wdaDriver.Orientation()
return s.WDADriver.Orientation()
}
// Tap Sends a tap event at the coordinate.
@@ -226,7 +209,7 @@ func (s *stubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error {
if err != nil {
return err
}
return s.wdaDriver.Tap(x, y, opts...)
return s.WDADriver.Tap(x, y, opts...)
}
// DoubleTap Sends a double tap event at the coordinate.
@@ -235,7 +218,7 @@ func (s *stubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) err
if err != nil {
return err
}
return s.wdaDriver.DoubleTap(x, y, opts...)
return s.WDADriver.DoubleTap(x, y, opts...)
}
// TouchAndHold Initiates a long-press gesture at the coordinate, holding for the specified duration.
@@ -246,7 +229,7 @@ func (s *stubIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption)
if err != nil {
return err
}
return s.wdaDriver.TouchAndHold(x, y, opts...)
return s.WDADriver.TouchAndHold(x, y, opts...)
}
// Drag Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
@@ -256,7 +239,7 @@ func (s *stubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.Acti
if err != nil {
return err
}
return s.wdaDriver.Drag(fromX, fromY, toX, toY, opts...)
return s.WDADriver.Drag(fromX, fromY, toX, toY, opts...)
}
// SetPasteboard Sets data to the general pasteboard
@@ -265,7 +248,7 @@ func (s *stubIOSDriver) SetPasteboard(contentType PasteboardType, content string
if err != nil {
return err
}
return s.wdaDriver.SetPasteboard(contentType, content)
return s.WDADriver.SetPasteboard(contentType, content)
}
// GetPasteboard Gets the data contained in the general pasteboard.
@@ -276,7 +259,7 @@ func (s *stubIOSDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Bu
if err != nil {
return nil, err
}
return s.wdaDriver.GetPasteboard(contentType)
return s.WDADriver.GetPasteboard(contentType)
}
func (s *stubIOSDriver) SetIme(ime string) error {
@@ -284,7 +267,7 @@ func (s *stubIOSDriver) SetIme(ime string) error {
if err != nil {
return err
}
return s.wdaDriver.SetIme(ime)
return s.WDADriver.SetIme(ime)
}
// SendKeys Types a string into active element. There must be element with keyboard focus,
@@ -295,7 +278,7 @@ func (s *stubIOSDriver) SendKeys(text string, opts ...option.ActionOption) error
if err != nil {
return err
}
return s.wdaDriver.SendKeys(text, opts...)
return s.WDADriver.SendKeys(text, opts...)
}
// Input works like SendKeys
@@ -304,7 +287,7 @@ func (s *stubIOSDriver) Input(text string, opts ...option.ActionOption) error {
if err != nil {
return err
}
return s.wdaDriver.Input(text, opts...)
return s.WDADriver.Input(text, opts...)
}
func (s *stubIOSDriver) Clear(packageName string) error {
@@ -312,7 +295,7 @@ func (s *stubIOSDriver) Clear(packageName string) error {
if err != nil {
return err
}
return s.wdaDriver.Clear(packageName)
return s.WDADriver.Clear(packageName)
}
// PressButton Presses the corresponding hardware button on the device
@@ -321,7 +304,7 @@ func (s *stubIOSDriver) PressButton(devBtn DeviceButton) error {
if err != nil {
return err
}
return s.wdaDriver.PressButton(devBtn)
return s.WDADriver.PressButton(devBtn)
}
// PressBack Presses the back button
@@ -330,7 +313,7 @@ func (s *stubIOSDriver) PressBack(opts ...option.ActionOption) error {
if err != nil {
return err
}
return s.wdaDriver.PressBack(opts...)
return s.WDADriver.PressBack(opts...)
}
func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
@@ -338,7 +321,7 @@ func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
if err != nil {
return err
}
return s.wdaDriver.PressKeyCode(keyCode)
return s.WDADriver.PressKeyCode(keyCode)
}
func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) {
@@ -346,7 +329,7 @@ func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) {
if err != nil {
return nil, err
}
return s.wdaDriver.Screenshot()
return s.WDADriver.Screenshot()
//screenshotService, err := instruments.NewScreenshotService(s.device.d)
//if err != nil {
// log.Error().Err(err).Msg("Starting screenshot service failed")
@@ -367,7 +350,7 @@ func (s *stubIOSDriver) TapByText(text string, opts ...option.ActionOption) erro
if err != nil {
return err
}
return s.wdaDriver.TapByText(text, opts...)
return s.WDADriver.TapByText(text, opts...)
}
func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error {
@@ -375,7 +358,7 @@ func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error {
if err != nil {
return err
}
return s.wdaDriver.TapByTexts(actions...)
return s.WDADriver.TapByTexts(actions...)
}
// AccessibleSource Return application elements accessibility tree
@@ -384,7 +367,7 @@ func (s *stubIOSDriver) AccessibleSource() (string, error) {
if err != nil {
return "", err
}
return s.wdaDriver.AccessibleSource()
return s.WDADriver.AccessibleSource()
}
// HealthCheck Health check might modify simulator state so it should only be called in-between testing sessions
@@ -397,7 +380,7 @@ func (s *stubIOSDriver) HealthCheck() error {
if err != nil {
return err
}
return s.wdaDriver.HealthCheck()
return s.WDADriver.HealthCheck()
}
func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
@@ -405,7 +388,7 @@ func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
if err != nil {
return nil, err
}
return s.wdaDriver.GetAppiumSettings()
return s.WDADriver.GetAppiumSettings()
}
func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) {
@@ -413,7 +396,7 @@ func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[
if err != nil {
return nil, err
}
return s.wdaDriver.SetAppiumSettings(settings)
return s.WDADriver.SetAppiumSettings(settings)
}
func (s *stubIOSDriver) IsHealthy() (bool, error) {
@@ -421,7 +404,7 @@ func (s *stubIOSDriver) IsHealthy() (bool, error) {
if err != nil {
return false, err
}
return s.wdaDriver.IsHealthy()
return s.WDADriver.IsHealthy()
}
// triggers the log capture and returns the log entries
@@ -430,7 +413,7 @@ func (s *stubIOSDriver) StartCaptureLog(identifier ...string) (err error) {
if err != nil {
return err
}
return s.wdaDriver.StartCaptureLog(identifier...)
return s.WDADriver.StartCaptureLog(identifier...)
}
func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) {
@@ -438,7 +421,7 @@ func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) {
if err != nil {
return nil, err
}
return s.wdaDriver.StopCaptureLog()
return s.WDADriver.StopCaptureLog()
}
func (s *stubIOSDriver) GetDriverResults() []*DriverRequests {
@@ -446,7 +429,7 @@ func (s *stubIOSDriver) GetDriverResults() []*DriverRequests {
if err != nil {
return nil
}
return s.wdaDriver.GetDriverResults()
return s.WDADriver.GetDriverResults()
}
func (s *stubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) {

View File

@@ -23,19 +23,31 @@ import (
"github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
)
type wdaDriver struct {
func NewWDADriver(device *IOSDevice) (*WDADriver, error) {
log.Info().Interface("device", device).Msg("init ios WDA driver")
driver := &WDADriver{
IOSDevice: device,
Session: &Session{},
}
driver.NewSession(nil)
return driver, nil
}
type WDADriver struct {
*IOSDevice
*Session
DriverExt
udid string
mjpegHTTPConn net.Conn // via HTTP
mjpegClient *http.Client
mjpegUrl string
}
func (wd *wdaDriver) resetSession() error {
func (wd *WDADriver) resetSession() error {
capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
@@ -59,7 +71,7 @@ func (wd *wdaDriver) resetSession() error {
return nil
}
func (wd *wdaDriver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) {
func (wd *WDADriver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) {
retryInterval := 3 * time.Second
for retryCount := 1; retryCount <= 3; retryCount++ {
rawResp, err = wd.Request(method, rawURL, rawBody)
@@ -93,11 +105,11 @@ func (wd *wdaDriver) httpRequest(method string, rawURL string, rawBody []byte) (
return
}
func (wd *wdaDriver) httpGET(pathElem ...string) (rawResp rawResponse, err error) {
func (wd *WDADriver) httpGET(pathElem ...string) (rawResp rawResponse, err error) {
return wd.httpRequest(http.MethodGet, wd.concatURL(nil, pathElem...), nil)
}
func (wd *wdaDriver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) {
func (wd *WDADriver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) {
var bsJSON []byte = nil
if data != nil {
if bsJSON, err = json.Marshal(data); err != nil {
@@ -107,15 +119,15 @@ func (wd *wdaDriver) httpPOST(data interface{}, pathElem ...string) (rawResp raw
return wd.httpRequest(http.MethodPost, wd.concatURL(nil, pathElem...), bsJSON)
}
func (wd *wdaDriver) httpDELETE(pathElem ...string) (rawResp rawResponse, err error) {
func (wd *WDADriver) httpDELETE(pathElem ...string) (rawResp rawResponse, err error) {
return wd.httpRequest(http.MethodDelete, wd.concatURL(nil, pathElem...), nil)
}
func (wd *wdaDriver) GetMjpegClient() *http.Client {
func (wd *WDADriver) GetMjpegClient() *http.Client {
return wd.mjpegClient
}
func (wd *wdaDriver) NewSession(capabilities option.Capabilities) (sessionInfo Session, err error) {
func (wd *WDADriver) NewSession(capabilities option.Capabilities) (sessionInfo Session, err error) {
// [[FBRoute POST:@"/session"].withoutSession respondWithTarget:self action:@selector(handleCreateSession:)]
data := make(map[string]interface{})
if len(capabilities) == 0 {
@@ -136,7 +148,7 @@ func (wd *wdaDriver) NewSession(capabilities option.Capabilities) (sessionInfo S
return
}
func (wd *wdaDriver) DeleteSession() (err error) {
func (wd *WDADriver) DeleteSession() (err error) {
if wd.mjpegClient != nil {
wd.mjpegClient.CloseIdleConnections()
}
@@ -149,7 +161,7 @@ func (wd *wdaDriver) DeleteSession() (err error) {
return
}
func (wd *wdaDriver) Status() (deviceStatus DeviceStatus, err error) {
func (wd *WDADriver) Status() (deviceStatus DeviceStatus, err error) {
// [[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)]
var rawResp rawResponse
// Notice: use Driver.GET instead of httpGET to avoid loop calling
@@ -164,7 +176,11 @@ func (wd *wdaDriver) Status() (deviceStatus DeviceStatus, err error) {
return
}
func (wd *wdaDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
func (wd *WDADriver) GetDevice() IDevice {
return wd.IOSDevice
}
func (wd *WDADriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
// [[FBRoute GET:@"/wda/device/info"] respondWithTarget:self action:@selector(handleGetDeviceInfo:)]
// [[FBRoute GET:@"/wda/device/info"].withoutSession
var rawResp rawResponse
@@ -179,7 +195,7 @@ func (wd *wdaDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
return
}
func (wd *wdaDriver) Location() (location Location, err error) {
func (wd *WDADriver) Location() (location Location, err error) {
// [[FBRoute GET:@"/wda/device/location"] respondWithTarget:self action:@selector(handleGetLocation:)]
// [[FBRoute GET:@"/wda/device/location"].withoutSession
var rawResp rawResponse
@@ -194,7 +210,7 @@ func (wd *wdaDriver) Location() (location Location, err error) {
return
}
func (wd *wdaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
func (wd *WDADriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
// [[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/batteryInfo"); err != nil {
@@ -208,7 +224,7 @@ func (wd *wdaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
return
}
func (wd *wdaDriver) WindowSize() (size Size, err error) {
func (wd *WDADriver) WindowSize() (size ai.Size, err error) {
// [[FBRoute GET:@"/window/size"] respondWithTarget:self action:@selector(handleGetWindowSize:)]
if !wd.windowSize.IsNil() {
// use cached window size
@@ -217,16 +233,16 @@ func (wd *wdaDriver) WindowSize() (size Size, err error) {
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/window/size"); err != nil {
return Size{}, errors.Wrap(err, "get window size failed by WDA request")
return ai.Size{}, errors.Wrap(err, "get window size failed by WDA request")
}
reply := new(struct{ Value struct{ Size } })
reply := new(struct{ Value struct{ ai.Size } })
if err = json.Unmarshal(rawResp, reply); err != nil {
return Size{}, errors.Wrap(err, "get window size failed by WDA response")
return ai.Size{}, errors.Wrap(err, "get window size failed by WDA response")
}
size = reply.Value.Size
scale, err := wd.Scale()
if err != nil {
return Size{}, errors.Wrap(err, "get window size scale failed")
return ai.Size{}, errors.Wrap(err, "get window size scale failed")
}
size.Height = size.Height * int(scale)
size.Width = size.Width * int(scale)
@@ -235,26 +251,21 @@ func (wd *wdaDriver) WindowSize() (size Size, err error) {
return wd.windowSize, nil
}
func (wd *wdaDriver) Screen() (screen Screen, err error) {
func (wd *WDADriver) Screen() (screen ai.Screen, err error) {
// [[FBRoute GET:@"/wda/screen"] respondWithTarget:self action:@selector(handleGetScreen:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/screen"); err != nil {
return Screen{}, err
return ai.Screen{}, err
}
reply := new(struct{ Value struct{ Screen } })
reply := new(struct{ Value struct{ ai.Screen } })
if err = json.Unmarshal(rawResp, reply); err != nil {
return Screen{}, err
return ai.Screen{}, err
}
screen = reply.Value.Screen
return
}
func (wd *wdaDriver) GetTimestamp() (timestamp int64, err error) {
return 0, errors.Wrap(errDriverNotImplemented,
"GetTimestamp not implemented for ios")
}
func (wd *wdaDriver) Scale() (float64, error) {
func (wd *WDADriver) Scale() (float64, error) {
if !builtin.IsZeroFloat64(wd.scale) {
return wd.scale, nil
}
@@ -266,11 +277,11 @@ func (wd *wdaDriver) Scale() (float64, error) {
return screen.Scale, nil
}
func (wd *wdaDriver) toScale(x float64) float64 {
func (wd *WDADriver) toScale(x float64) float64 {
return x / wd.scale
}
func (wd *wdaDriver) ActiveAppInfo() (info AppInfo, err error) {
func (wd *WDADriver) ActiveAppInfo() (info AppInfo, err error) {
// [[FBRoute GET:@"/wda/activeAppInfo"] respondWithTarget:self action:@selector(handleActiveAppInfo:)]
// [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession
var rawResp rawResponse
@@ -285,7 +296,7 @@ func (wd *wdaDriver) ActiveAppInfo() (info AppInfo, err error) {
return
}
func (wd *wdaDriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
func (wd *WDADriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
// [[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil {
@@ -299,7 +310,7 @@ func (wd *wdaDriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
return
}
func (wd *wdaDriver) AppState(bundleId string) (runState AppState, err error) {
func (wd *WDADriver) AppState(bundleId string) (runState AppState, err error) {
// [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)]
data := map[string]interface{}{"bundleId": bundleId}
var rawResp rawResponse
@@ -315,7 +326,7 @@ func (wd *wdaDriver) AppState(bundleId string) (runState AppState, err error) {
return
}
func (wd *wdaDriver) IsLocked() (locked bool, err error) {
func (wd *WDADriver) IsLocked() (locked bool, err error) {
// [[FBRoute GET:@"/wda/locked"] respondWithTarget:self action:@selector(handleIsLocked:)]
// [[FBRoute GET:@"/wda/locked"].withoutSession
var rawResp rawResponse
@@ -328,27 +339,27 @@ func (wd *wdaDriver) IsLocked() (locked bool, err error) {
return
}
func (wd *wdaDriver) Unlock() (err error) {
func (wd *WDADriver) Unlock() (err error) {
// [[FBRoute POST:@"/wda/unlock"] respondWithTarget:self action:@selector(handleUnlock:)]
// [[FBRoute POST:@"/wda/unlock"].withoutSession
_, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/unlock")
return
}
func (wd *wdaDriver) Lock() (err error) {
func (wd *WDADriver) Lock() (err error) {
// [[FBRoute POST:@"/wda/lock"] respondWithTarget:self action:@selector(handleLock:)]
// [[FBRoute POST:@"/wda/lock"].withoutSession
_, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/lock")
return
}
func (wd *wdaDriver) Homescreen() (err error) {
func (wd *WDADriver) Homescreen() (err error) {
// [[FBRoute POST:@"/wda/homescreen"].withoutSession respondWithTarget:self action:@selector(handleHomescreenCommand:)]
_, err = wd.httpPOST(nil, "/wda/homescreen")
return
}
func (wd *wdaDriver) AlertText() (text string, err error) {
func (wd *WDADriver) AlertText() (text string, err error) {
// [[FBRoute GET:@"/alert/text"] respondWithTarget:self action:@selector(handleAlertGetTextCommand:)]
// [[FBRoute GET:@"/alert/text"].withoutSession
var rawResp rawResponse
@@ -361,7 +372,7 @@ func (wd *wdaDriver) AlertText() (text string, err error) {
return
}
func (wd *wdaDriver) AlertButtons() (btnLabels []string, err error) {
func (wd *WDADriver) AlertButtons() (btnLabels []string, err error) {
// [[FBRoute GET:@"/wda/alert/buttons"] respondWithTarget:self action:@selector(handleGetAlertButtonsCommand:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/alert/buttons"); err != nil {
@@ -375,7 +386,7 @@ func (wd *wdaDriver) AlertButtons() (btnLabels []string, err error) {
return
}
func (wd *wdaDriver) AlertAccept(label ...string) (err error) {
func (wd *WDADriver) AlertAccept(label ...string) (err error) {
// [[FBRoute POST:@"/alert/accept"] respondWithTarget:self action:@selector(handleAlertAcceptCommand:)]
// [[FBRoute POST:@"/alert/accept"].withoutSession
data := make(map[string]interface{})
@@ -386,7 +397,7 @@ func (wd *wdaDriver) AlertAccept(label ...string) (err error) {
return
}
func (wd *wdaDriver) AlertDismiss(label ...string) (err error) {
func (wd *WDADriver) AlertDismiss(label ...string) (err error) {
// [[FBRoute POST:@"/alert/dismiss"] respondWithTarget:self action:@selector(handleAlertDismissCommand:)]
// [[FBRoute POST:@"/alert/dismiss"].withoutSession
data := make(map[string]interface{})
@@ -397,14 +408,14 @@ func (wd *wdaDriver) AlertDismiss(label ...string) (err error) {
return
}
func (wd *wdaDriver) AlertSendKeys(text string) (err error) {
func (wd *WDADriver) AlertSendKeys(text string) (err error) {
// [[FBRoute POST:@"/alert/text"] respondWithTarget:self action:@selector(handleAlertSetTextCommand:)]
data := map[string]interface{}{"value": strings.Split(text, "")}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/alert/text")
return
}
func (wd *wdaDriver) AppLaunch(bundleId string) (err error) {
func (wd *WDADriver) AppLaunch(bundleId string) (err error) {
// [[FBRoute POST:@"/wda/apps/launch"] respondWithTarget:self action:@selector(handleSessionAppLaunch:)]
data := make(map[string]interface{})
data["bundleId"] = bundleId
@@ -419,7 +430,7 @@ func (wd *wdaDriver) AppLaunch(bundleId string) (err error) {
return nil
}
func (wd *wdaDriver) AppLaunchUnattached(bundleId string) (err error) {
func (wd *WDADriver) AppLaunchUnattached(bundleId string) (err error) {
// [[FBRoute POST:@"/wda/apps/launchUnattached"].withoutSession respondWithTarget:self action:@selector(handleLaunchUnattachedApp:)]
data := map[string]interface{}{"bundleId": bundleId}
_, err = wd.httpPOST(data, "/wda/apps/launchUnattached")
@@ -430,7 +441,7 @@ func (wd *wdaDriver) AppLaunchUnattached(bundleId string) (err error) {
return nil
}
func (wd *wdaDriver) AppTerminate(bundleId string) (successful bool, err error) {
func (wd *WDADriver) AppTerminate(bundleId string) (successful bool, err error) {
// [[FBRoute POST:@"/wda/apps/terminate"] respondWithTarget:self action:@selector(handleSessionAppTerminate:)]
data := map[string]interface{}{"bundleId": bundleId}
var rawResp rawResponse
@@ -443,14 +454,14 @@ func (wd *wdaDriver) AppTerminate(bundleId string) (successful bool, err error)
return
}
func (wd *wdaDriver) AppActivate(bundleId string) (err error) {
func (wd *WDADriver) AppActivate(bundleId string) (err error) {
// [[FBRoute POST:@"/wda/apps/activate"] respondWithTarget:self action:@selector(handleSessionAppActivate:)]
data := map[string]interface{}{"bundleId": bundleId}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/activate")
return
}
func (wd *wdaDriver) AppDeactivate(second float64) (err error) {
func (wd *WDADriver) AppDeactivate(second float64) (err error) {
// [[FBRoute POST:@"/wda/deactivateApp"] respondWithTarget:self action:@selector(handleDeactivateAppCommand:)]
if second < 3 {
second = 3.0
@@ -460,7 +471,7 @@ func (wd *wdaDriver) AppDeactivate(second float64) (err error) {
return
}
func (wd *wdaDriver) GetForegroundApp() (appInfo AppInfo, err error) {
func (wd *WDADriver) GetForegroundApp() (appInfo AppInfo, err error) {
activeAppInfo, err := wd.ActiveAppInfo()
appInfo.BundleId = activeAppInfo.BundleId
if err != nil {
@@ -484,7 +495,7 @@ func (wd *wdaDriver) GetForegroundApp() (appInfo AppInfo, err error) {
return appInfo, err
}
func (wd *wdaDriver) AssertForegroundApp(bundleId string, viewControllerType ...string) error {
func (wd *WDADriver) AssertForegroundApp(bundleId string, viewControllerType ...string) error {
log.Debug().Str("bundleId", bundleId).
Strs("viewControllerType", viewControllerType).
Msg("assert ios foreground bundleId")
@@ -507,7 +518,7 @@ func (wd *wdaDriver) AssertForegroundApp(bundleId string, viewControllerType ...
return nil
}
func (wd *wdaDriver) Tap(x, y float64, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) Tap(x, y float64, opts ...option.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/tap/:uuid"] respondWithTarget:self action:@selector(handleTap:)]
actionOptions := option.NewActionOptions(opts...)
@@ -531,7 +542,7 @@ func (wd *wdaDriver) Tap(x, y float64, opts ...option.ActionOption) (err error)
return
}
func (wd *wdaDriver) DoubleTap(x, y float64, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) DoubleTap(x, y float64, opts ...option.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/doubleTap"] respondWithTarget:self action:@selector(handleDoubleTapCoordinate:)]
actionOptions := option.NewActionOptions(opts...)
x = wd.toScale(x)
@@ -551,7 +562,7 @@ func (wd *wdaDriver) DoubleTap(x, y float64, opts ...option.ActionOption) (err e
return
}
func (wd *wdaDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) TouchAndHold(x, y float64, opts ...option.ActionOption) (err error) {
actionOptions := option.NewActionOptions(opts...)
if actionOptions.Duration == 0 {
opts = append(opts, option.WithDuration(1))
@@ -559,7 +570,7 @@ func (wd *wdaDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) (er
return wd.Tap(x, y, opts...)
}
func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/dragfromtoforduration"] respondWithTarget:self action:@selector(handleDragCoordinate:)]
actionOptions := option.NewActionOptions(opts...)
@@ -596,11 +607,11 @@ func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionO
return
}
func (wd *wdaDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
func (wd *WDADriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
return wd.Drag(fromX, fromY, toX, toY, opts...)
}
func (wd *wdaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
func (wd *WDADriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
// [[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)]
data := map[string]interface{}{
"contentType": contentType,
@@ -610,7 +621,7 @@ func (wd *wdaDriver) SetPasteboard(contentType PasteboardType, content string) (
return
}
func (wd *wdaDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) {
func (wd *WDADriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) {
// [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)]
data := map[string]interface{}{"contentType": contentType}
var rawResp rawResponse
@@ -623,15 +634,15 @@ func (wd *wdaDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffe
return
}
func (wd *wdaDriver) SetIme(ime string) error {
func (wd *WDADriver) SetIme(ime string) error {
return errDriverNotImplemented
}
func (wd *wdaDriver) PressKeyCode(keyCode KeyCode) (err error) {
func (wd *WDADriver) PressKeyCode(keyCode KeyCode) (err error) {
return errDriverNotImplemented
}
func (wd *wdaDriver) SendKeys(text string, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) SendKeys(text string, opts ...option.ActionOption) (err error) {
// [[FBRoute POST:@"/wda/keys"] respondWithTarget:self action:@selector(handleKeys:)]
actionOptions := option.NewActionOptions(opts...)
data := map[string]interface{}{"value": strings.Split(text, "")}
@@ -643,7 +654,7 @@ func (wd *wdaDriver) SendKeys(text string, opts ...option.ActionOption) (err err
return
}
func (wd *wdaDriver) Backspace(count int, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) Backspace(count int, opts ...option.ActionOption) (err error) {
if count == 0 {
return nil
}
@@ -657,16 +668,16 @@ func (wd *wdaDriver) Backspace(count int, opts ...option.ActionOption) (err erro
return
}
func (wd *wdaDriver) Input(text string, opts ...option.ActionOption) (err error) {
func (wd *WDADriver) Input(text string, opts ...option.ActionOption) (err error) {
return wd.SendKeys(text, opts...)
}
func (wd *wdaDriver) Clear(packageName string) error {
func (wd *WDADriver) Clear(packageName string) error {
return errDriverNotImplemented
}
// PressBack simulates a short press on the BACK button.
func (wd *wdaDriver) PressBack(opts ...option.ActionOption) (err error) {
func (wd *WDADriver) PressBack(opts ...option.ActionOption) (err error) {
actionOptions := option.NewActionOptions(opts...)
windowSize, err := wd.WindowSize()
@@ -702,27 +713,27 @@ func (wd *wdaDriver) PressBack(opts ...option.ActionOption) (err error) {
return
}
func (wd *wdaDriver) PressButton(devBtn DeviceButton) (err error) {
func (wd *WDADriver) PressButton(devBtn DeviceButton) (err error) {
// [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)]
data := map[string]interface{}{"name": devBtn}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton")
return
}
func (wd *wdaDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
func (wd *WDADriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
return info, errDriverNotImplemented
}
func (wd *wdaDriver) LogoutNoneUI(packageName string) error {
func (wd *WDADriver) LogoutNoneUI(packageName string) error {
return errDriverNotImplemented
}
func (wd *wdaDriver) StartCamera() (err error) {
func (wd *WDADriver) StartCamera() (err error) {
// start camera, alias for app_launch com.apple.camera
return wd.AppLaunch("com.apple.camera")
}
func (wd *wdaDriver) StopCamera() (err error) {
func (wd *WDADriver) StopCamera() (err error) {
// stop camera, alias for app_terminate com.apple.camera
success, err := wd.AppTerminate("com.apple.camera")
if err != nil {
@@ -734,7 +745,7 @@ func (wd *wdaDriver) StopCamera() (err error) {
return nil
}
func (wd *wdaDriver) Orientation() (orientation Orientation, err error) {
func (wd *WDADriver) Orientation() (orientation Orientation, err error) {
// [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil {
@@ -748,14 +759,14 @@ func (wd *wdaDriver) Orientation() (orientation Orientation, err error) {
return
}
func (wd *wdaDriver) SetOrientation(orientation Orientation) (err error) {
func (wd *WDADriver) SetOrientation(orientation Orientation) (err error) {
// [[FBRoute POST:@"/orientation"] respondWithTarget:self action:@selector(handleSetOrientation:)]
data := map[string]interface{}{"orientation": orientation}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation")
return
}
func (wd *wdaDriver) Rotation() (rotation Rotation, err error) {
func (wd *WDADriver) Rotation() (rotation Rotation, err error) {
// [[FBRoute GET:@"/rotation"] respondWithTarget:self action:@selector(handleGetRotation:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/rotation"); err != nil {
@@ -769,13 +780,13 @@ func (wd *wdaDriver) Rotation() (rotation Rotation, err error) {
return
}
func (wd *wdaDriver) SetRotation(rotation Rotation) (err error) {
func (wd *WDADriver) SetRotation(rotation Rotation) (err error) {
// [[FBRoute POST:@"/rotation"] respondWithTarget:self action:@selector(handleSetRotation:)]
_, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation")
return
}
func (wd *wdaDriver) Screenshot() (raw *bytes.Buffer, err error) {
func (wd *WDADriver) Screenshot() (raw *bytes.Buffer, err error) {
// [[FBRoute GET:@"/screenshot"] respondWithTarget:self action:@selector(handleGetScreenshot:)]
// [[FBRoute GET:@"/screenshot"].withoutSession respondWithTarget:self action:@selector(handleGetScreenshot:)]
var rawResp rawResponse
@@ -791,7 +802,7 @@ func (wd *wdaDriver) Screenshot() (raw *bytes.Buffer, err error) {
return
}
func (wd *wdaDriver) Source(srcOpt ...option.SourceOption) (source string, err error) {
func (wd *WDADriver) Source(srcOpt ...option.SourceOption) (source string, err error) {
// [[FBRoute GET:@"/source"] respondWithTarget:self action:@selector(handleGetSourceCommand:)]
// [[FBRoute GET:@"/source"].withoutSession
tmp, _ := url.Parse(wd.concatURL(nil, "/session", wd.sessionID))
@@ -825,15 +836,15 @@ func (wd *wdaDriver) Source(srcOpt ...option.SourceOption) (source string, err e
return
}
func (wd *wdaDriver) TapByText(text string, opts ...option.ActionOption) error {
func (wd *WDADriver) TapByText(text string, opts ...option.ActionOption) error {
return errDriverNotImplemented
}
func (wd *wdaDriver) TapByTexts(actions ...TapTextAction) error {
func (wd *WDADriver) TapByTexts(actions ...TapTextAction) error {
return errDriverNotImplemented
}
func (wd *wdaDriver) AccessibleSource() (source string, err error) {
func (wd *WDADriver) AccessibleSource() (source string, err error) {
// [[FBRoute GET:@"/wda/accessibleSource"] respondWithTarget:self action:@selector(handleGetAccessibleSourceCommand:)]
// [[FBRoute GET:@"/wda/accessibleSource"].withoutSession
var rawResp rawResponse
@@ -848,13 +859,13 @@ func (wd *wdaDriver) AccessibleSource() (source string, err error) {
return
}
func (wd *wdaDriver) HealthCheck() (err error) {
func (wd *WDADriver) HealthCheck() (err error) {
// [[FBRoute GET:@"/wda/healthcheck"].withoutSession respondWithTarget:self action:@selector(handleGetHealthCheck:)]
_, err = wd.httpGET("/wda/healthcheck")
return
}
func (wd *wdaDriver) GetAppiumSettings() (settings map[string]interface{}, err error) {
func (wd *WDADriver) GetAppiumSettings() (settings map[string]interface{}, err error) {
// [[FBRoute GET:@"/appium/settings"] respondWithTarget:self action:@selector(handleGetSettings:)]
var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/appium/settings"); err != nil {
@@ -868,7 +879,7 @@ func (wd *wdaDriver) GetAppiumSettings() (settings map[string]interface{}, err e
return
}
func (wd *wdaDriver) SetAppiumSettings(settings map[string]interface{}) (ret map[string]interface{}, err error) {
func (wd *WDADriver) SetAppiumSettings(settings map[string]interface{}) (ret map[string]interface{}, err error) {
// [[FBRoute POST:@"/appium/settings"] respondWithTarget:self action:@selector(handleSetSettings:)]
data := map[string]interface{}{"settings": settings}
var rawResp rawResponse
@@ -883,7 +894,7 @@ func (wd *wdaDriver) SetAppiumSettings(settings map[string]interface{}) (ret map
return
}
func (wd *wdaDriver) IsHealthy() (healthy bool, err error) {
func (wd *WDADriver) IsHealthy() (healthy bool, err error) {
var rawResp rawResponse
if rawResp, err = wd.httpGET("/health"); err != nil {
return false, err
@@ -894,17 +905,17 @@ func (wd *wdaDriver) IsHealthy() (healthy bool, err error) {
return true, nil
}
func (wd *wdaDriver) WdaShutdown() (err error) {
func (wd *WDADriver) WdaShutdown() (err error) {
_, err = wd.httpGET("/wda/shutdown")
return
}
func (wd *wdaDriver) triggerWDALog(data map[string]interface{}) (rawResp []byte, err error) {
func (wd *WDADriver) triggerWDALog(data map[string]interface{}) (rawResp []byte, err error) {
// [[FBRoute POST:@"/gtf/automation/log"].withoutSession respondWithTarget:self action:@selector(handleAutomationLog:)]
return wd.httpPOST(data, "/gtf/automation/log")
}
func (wd *wdaDriver) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) {
func (wd *WDADriver) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) {
// 获取当前时间戳
timestamp := time.Now().Format("20060102_150405") + fmt.Sprintf("_%03d", time.Now().UnixNano()/1e6%1000)
// 创建文件名
@@ -968,7 +979,7 @@ func (wd *wdaDriver) RecordScreen(folderPath string, duration time.Duration) (vi
return filepath.Abs(fileName)
}
func (wd *wdaDriver) StartCaptureLog(identifier ...string) error {
func (wd *WDADriver) StartCaptureLog(identifier ...string) error {
log.Info().Msg("start WDA log recording")
if identifier == nil {
identifier = []string{""}
@@ -988,7 +999,7 @@ type wdaResponse struct {
SessionID string `json:"sessionId"`
}
func (wd *wdaDriver) StopCaptureLog() (result interface{}, err error) {
func (wd *WDADriver) StopCaptureLog() (result interface{}, err error) {
log.Info().Msg("stop log recording")
data := map[string]interface{}{"action": "stop"}
rawResp, err := wd.triggerWDALog(data)
@@ -1007,18 +1018,22 @@ func (wd *wdaDriver) StopCaptureLog() (result interface{}, err error) {
return reply.Value, nil
}
func (wd *wdaDriver) GetSession() *Session {
func (wd *WDADriver) GetSession() *Session {
return wd.Session
}
func (wd *wdaDriver) GetDriverResults() []*DriverRequests {
func (wd *WDADriver) GetDriverResults() []*DriverRequests {
defer func() {
wd.requests = nil
}()
return wd.requests
}
func (wd *wdaDriver) TearDown() error {
func (wd *WDADriver) Setup() error {
return nil
}
func (wd *WDADriver) TearDown() error {
wd.mjpegClient.CloseIdleConnections()
wd.client.CloseIdleConnections()
return nil

View File

@@ -471,7 +471,7 @@ func Test_remoteWD_AccessibleSource(t *testing.T) {
func TestRecord(t *testing.T) {
setup(t)
path, err := driver.(*wdaDriver).RecordScreen("", 5*time.Second)
path, err := driver.(*WDADriver).RecordScreen("", 5*time.Second)
if err != nil {
t.Fatal(err)
}

View File

@@ -260,6 +260,9 @@ func NewActionOptions(opts ...ActionOption) *ActionOptions {
for _, option := range opts {
option(actionOptions)
}
if actionOptions.MaxRetryTimes == 0 {
actionOptions.MaxRetryTimes = 1
}
return actionOptions
}

View File

@@ -1,6 +1,8 @@
package option
import "github.com/httprunner/funplugin"
import (
"github.com/httprunner/funplugin"
)
type DriverOptions struct {
Capabilities Capabilities

View File

@@ -2,7 +2,6 @@ package uixt
import (
"fmt"
"math"
)
type DeviceStatus struct {
@@ -153,20 +152,6 @@ func (bs BatteryStatus) String() string {
}
}
type Size struct {
Width int `json:"width"`
Height int `json:"height"`
}
func (s Size) IsNil() bool {
return s.Width == 0 && s.Height == 0
}
type Screen struct {
StatusBarSize Size `json:"statusBarSize"`
Scale float64 `json:"scale"`
}
type AppInfo struct {
Name string `json:"name,omitempty"`
AppBaseInfo
@@ -274,18 +259,3 @@ const (
DirectionLeft Direction = "left"
DirectionRight Direction = "right"
)
type Point struct {
X int `json:"x"` // upper left X coordinate of selected element
Y int `json:"y"` // upper left Y coordinate of selected element
}
type PointF struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
func (p PointF) IsIdentical(p2 PointF) bool {
// set the coordinate precision to 1 pixel
return math.Abs(p.X-p2.X) < 1 && math.Abs(p.Y-p2.Y) < 1
}