refactor: split ai related logic to pkg/ai

This commit is contained in:
lilong.129
2025-02-08 14:46:24 +08:00
parent 06d7a7e721
commit 5e45eb7836
32 changed files with 798 additions and 487 deletions
+1 -1
View File
@@ -1 +1 @@
v5.0.0+2502071814 v5.0.0+2502081446
+63
View File
@@ -0,0 +1,63 @@
package ai
import (
"os"
"github.com/httprunner/httprunner/v5/code"
"github.com/rs/zerolog/log"
)
func NewAIService(opts ...AIServiceOption) *AIServices {
services := &AIServices{}
for _, option := range opts {
option(services)
}
return services
}
type AIServices struct {
ICVService
ILLMService
}
type AIServiceOption func(*AIServices)
type CVServiceType string
const (
CVServiceTypeVEDEM CVServiceType = "vedem"
CVServiceTypeOpenCV CVServiceType = "opencv"
)
func WithCVService(service CVServiceType) AIServiceOption {
return func(opts *AIServices) {
if service == CVServiceTypeVEDEM {
var err error
opts.ICVService, err = NewVEDEMImageService()
if err != nil {
log.Error().Err(err).Msg("init vedem image service failed")
os.Exit(code.GetErrorCode(err))
}
}
}
}
type LLMServiceType string
const (
LLMServiceTypeGPT4o LLMServiceType = "gpt-4o"
LLMServiceTypeDeepSeekV3 LLMServiceType = "deepseek-v3"
)
func WithLLMService(service LLMServiceType) AIServiceOption {
return func(opts *AIServices) {
if service == LLMServiceTypeGPT4o {
var err error
opts.ILLMService, err = NewGPT4oLLMService()
if err != nil {
log.Error().Err(err).Msg("init gpt-4o llm service failed")
os.Exit(code.GetErrorCode(err))
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
package ai
import "testing"
func TestOption(t *testing.T) {
options := NewAIService(
WithCVService(CVServiceTypeOpenCV),
WithLLMService(LLMServiceTypeDeepSeekV3),
)
t.Log(options)
}
+51 -24
View File
@@ -1,4 +1,4 @@
package uixt package ai
import ( import (
"bytes" "bytes"
@@ -7,19 +7,18 @@ import (
"math" "math"
"regexp" "regexp"
"github.com/pkg/errors"
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/pkg/errors"
) )
type IImageService interface { type ICVService interface {
// GetImage returns image result including ocr texts, uploaded image url, etc // returns CV result including ocr texts, uploaded image url, etc
GetImage(imageBuf *bytes.Buffer, opts ...option.ActionOption) (imageResult *ImageResult, err error) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...ScreenShotOption) (*CVResult, error)
ReadFromPath(imagePath string, opts ...ScreenShotOption) (*CVResult, error)
} }
type ImageResult struct { type CVResult struct {
URL string `json:"url,omitempty"` // image uploaded url URL string `json:"url,omitempty"` // image uploaded url
OCRResult OCRResults `json:"ocrResult,omitempty"` // OCR texts OCRResult OCRResults `json:"ocrResult,omitempty"` // OCR texts
// NoLive(非直播间) // NoLive(非直播间)
@@ -106,7 +105,7 @@ func (t OCRTexts) texts() (texts []string) {
return texts return texts
} }
func (t OCRTexts) FilterScope(scope option.AbsScope) (results OCRTexts) { func (t OCRTexts) FilterScope(scope AbsScope) (results OCRTexts) {
for _, ocrText := range t { for _, ocrText := range t {
rect := ocrText.Rect rect := ocrText.Rect
@@ -128,12 +127,12 @@ func (t OCRTexts) FilterScope(scope option.AbsScope) (results OCRTexts) {
// FindText returns matched text with options // FindText returns matched text with options
// Notice: filter scope should be specified with WithAbsScope // Notice: filter scope should be specified with WithAbsScope
func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCRText, err error) { func (t OCRTexts) FindText(text string, opts ...ScreenFilterOption) (result OCRText, err error) {
actionOptions := option.NewActionOptions(opts...) options := NewScreenFilterOptions(opts...)
var results []OCRText var results []OCRText
for _, ocrText := range t.FilterScope(actionOptions.AbsScope) { for _, ocrText := range t.FilterScope(options.AbsScope) {
if actionOptions.Regex { if options.Regex {
// regex on, check if match regex // regex on, check if match regex
if !regexp.MustCompile(text).MatchString(ocrText.Text) { if !regexp.MustCompile(text).MatchString(ocrText.Text) {
continue continue
@@ -148,7 +147,7 @@ func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCR
results = append(results, ocrText) results = append(results, ocrText)
// return the first one matched exactly when index not specified // return the first one matched exactly when index not specified
if ocrText.Text == text && actionOptions.Index == 0 { if ocrText.Text == text && options.Index == 0 {
return ocrText, nil return ocrText, nil
} }
} }
@@ -159,7 +158,7 @@ func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCR
} }
// get index // get index
idx := actionOptions.Index idx := options.Index
if idx < 0 { if idx < 0 {
idx = len(results) + idx idx = len(results) + idx
} }
@@ -173,8 +172,8 @@ func (t OCRTexts) FindText(text string, opts ...option.ActionOption) (result OCR
return results[idx], nil return results[idx], nil
} }
func (t OCRTexts) FindTexts(texts []string, opts ...option.ActionOption) (results OCRTexts, err error) { func (t OCRTexts) FindTexts(texts []string, opts ...ScreenFilterOption) (results OCRTexts, err error) {
actionOptions := option.NewActionOptions(opts...) options := NewScreenFilterOptions(opts...)
for _, text := range texts { for _, text := range texts {
ocrText, err := t.FindText(text, opts...) ocrText, err := t.FindText(text, opts...)
if err != nil { if err != nil {
@@ -183,7 +182,7 @@ func (t OCRTexts) FindTexts(texts []string, opts ...option.ActionOption) (result
results = append(results, ocrText) results = append(results, ocrText)
// found one, skip searching and return // found one, skip searching and return
if actionOptions.MatchOne { if options.MatchOne {
return results, nil return results, nil
} }
} }
@@ -240,7 +239,7 @@ func (box Box) Center() PointF {
type UIResults []UIResult type UIResults []UIResult
func (u UIResults) FilterScope(scope option.AbsScope) (results UIResults) { func (u UIResults) FilterScope(scope AbsScope) (results UIResults) {
for _, uiResult := range u { for _, uiResult := range u {
rect := image.Rectangle{ rect := image.Rectangle{
Min: image.Point{ Min: image.Point{
@@ -268,16 +267,15 @@ func (u UIResults) FilterScope(scope option.AbsScope) (results UIResults) {
return return
} }
func (u UIResults) GetUIResult(opts ...option.ActionOption) (UIResult, error) { func (u UIResults) GetUIResult(opts ...ScreenFilterOption) (UIResult, error) {
actionOptions := option.NewActionOptions(opts...) options := NewScreenFilterOptions(opts...)
uiResults := u.FilterScope(options.AbsScope)
uiResults := u.FilterScope(actionOptions.AbsScope)
if len(uiResults) == 0 { if len(uiResults) == 0 {
return UIResult{}, errors.Wrap(code.CVResultNotFoundError, return UIResult{}, errors.Wrap(code.CVResultNotFoundError,
"ui types not found in scope") "ui types not found in scope")
} }
// get index // get index
idx := actionOptions.Index idx := options.Index
if idx < 0 { if idx < 0 {
idx = len(uiResults) + idx idx = len(uiResults) + idx
} }
@@ -301,3 +299,32 @@ type ClosePopupsResult struct {
func (c ClosePopupsResult) IsEmpty() bool { func (c ClosePopupsResult) IsEmpty() bool {
return c.PopupArea.IsEmpty() && c.CloseArea.IsEmpty() return c.PopupArea.IsEmpty() && c.CloseArea.IsEmpty()
} }
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
}
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"`
}
+25 -17
View File
@@ -1,4 +1,4 @@
package uixt package ai
import ( import (
"bytes" "bytes"
@@ -16,7 +16,6 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
var client = &http.Client{ var client = &http.Client{
@@ -24,19 +23,19 @@ var client = &http.Client{
} }
type APIResponseImage struct { type APIResponseImage struct {
Code int `json:"code"` Code int `json:"code"`
Message string `json:"message"` Message string `json:"message"`
Result ImageResult `json:"result"` Result CVResult `json:"result"`
} }
func newVEDEMImageService() (*veDEMImageService, error) { func NewVEDEMImageService() (*vedemCVService, error) {
if err := checkEnv(); err != nil { if err := checkEnv(); err != nil {
return nil, err return nil, err
} }
return &veDEMImageService{}, nil return &vedemCVService{}, nil
} }
// veDEMImageService implements IImageService interface // vedemCVService implements IImageService interface
// actions: // actions:
// //
// ocr - get ocr texts // ocr - get ocr texts
@@ -45,11 +44,24 @@ func newVEDEMImageService() (*veDEMImageService, error) {
// popup - get popup windows // popup - get popup windows
// close - get close popup // close - get close popup
// ui - get ui position by type(s) // ui - get ui position by type(s)
type veDEMImageService struct{} type vedemCVService struct{}
func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, opts ...option.ActionOption) (imageResult *ImageResult, err error) { func (s *vedemCVService) ReadFromPath(imagePath string, opts ...ScreenShotOption) (
actionOptions := option.NewActionOptions(opts...) imageResult *CVResult, err error) {
screenshotActions := actionOptions.ScreenshotActions() imageBuf, err := os.ReadFile(imagePath)
if err != nil {
err = errors.Wrap(code.CVRequestError,
fmt.Sprintf("read image file error: %v", err))
return
}
imageResult, err = s.ReadFromBuffer(bytes.NewBuffer(imageBuf), opts...)
return
}
func (s *vedemCVService) ReadFromBuffer(imageBuf *bytes.Buffer, opts ...ScreenShotOption) (
imageResult *CVResult, err error) {
actionOptions := NewScreenShotOptions(opts...)
screenshotActions := actionOptions.List()
if len(screenshotActions) == 0 { if len(screenshotActions) == 0 {
// skip // skip
return nil, nil return nil, nil
@@ -98,11 +110,7 @@ func (s *veDEMImageService) GetImage(imageBuf *bytes.Buffer, opts ...option.Acti
bodyWriter.WriteField("ocrCluster", actionOptions.ScreenShotWithOCRCluster) bodyWriter.WriteField("ocrCluster", actionOptions.ScreenShotWithOCRCluster)
} }
if actionOptions.Timeout > 0 { bodyWriter.WriteField("timeout", fmt.Sprintf("%v", 10))
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", actionOptions.Timeout))
} else {
bodyWriter.WriteField("timeout", fmt.Sprintf("%v", 10))
}
formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png") formWriter, err := bodyWriter.CreateFormFile("image", "screenshot.png")
if err != nil { if err != nil {
+41
View File
@@ -0,0 +1,41 @@
//go:build localtest
package ai
import (
"bytes"
"fmt"
"os"
"testing"
)
func TestGetImageFromBuffer(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)
service := NewAIService(
WithCVService(CVServiceTypeVEDEM),
)
cvResult, err := service.ReadFromBuffer(buf)
if err != nil {
t.Fatal(err)
}
fmt.Println(fmt.Sprintf("cvResult: %v", cvResult))
}
func TestGetImageFromPath(t *testing.T) {
imagePath := "/Users/debugtalk/Downloads/s1.png"
service := NewAIService(
WithCVService(CVServiceTypeVEDEM),
)
cvResult, err := service.ReadFromPath(imagePath)
if err != nil {
t.Fatal(err)
}
fmt.Println(fmt.Sprintf("cvResult: %v", cvResult))
}
+20
View File
@@ -0,0 +1,20 @@
package ai
import "context"
type ILLMService interface {
Call(ctx context.Context, prompt string) (string, error)
}
func NewGPT4oLLMService() (*openaiLLMService, error) {
if err := checkEnv(); err != nil {
return nil, err
}
return &openaiLLMService{}, nil
}
type openaiLLMService struct{}
func (s openaiLLMService) Call(ctx context.Context, prompt string) (string, error) {
return "", nil
}
+213
View File
@@ -0,0 +1,213 @@
package ai
func NewScreenShotOptions(opts ...ScreenShotOption) *ScreenShotOptions {
options := &ScreenShotOptions{}
for _, option := range opts {
option(options)
}
return options
}
type ScreenShotOptions struct {
ScreenShotWithOCR bool `json:"screenshot_with_ocr,omitempty" yaml:"screenshot_with_ocr,omitempty"`
ScreenShotWithUpload bool `json:"screenshot_with_upload,omitempty" yaml:"screenshot_with_upload,omitempty"`
ScreenShotWithLiveType bool `json:"screenshot_with_live_type,omitempty" yaml:"screenshot_with_live_type,omitempty"`
ScreenShotWithLivePopularity bool `json:"screenshot_with_live_popularity,omitempty" yaml:"screenshot_with_live_popularity,omitempty"`
ScreenShotWithUITypes []string `json:"screenshot_with_ui_types,omitempty" yaml:"screenshot_with_ui_types,omitempty"`
ScreenShotWithClosePopups bool `json:"screenshot_with_close_popups,omitempty" yaml:"screenshot_with_close_popups,omitempty"`
ScreenShotWithOCRCluster string `json:"screenshot_with_ocr_cluster,omitempty" yaml:"screenshot_with_ocr_cluster,omitempty"`
ScreenShotFileName string `json:"screenshot_file_name,omitempty" yaml:"screenshot_file_name,omitempty"`
}
func (o *ScreenShotOptions) Options() []ScreenShotOption {
options := make([]ScreenShotOption, 0)
if o == nil {
return options
}
// screenshot options
if o.ScreenShotWithOCR {
options = append(options, WithScreenShotOCR(true))
}
if o.ScreenShotWithUpload {
options = append(options, WithScreenShotUpload(true))
}
if o.ScreenShotWithLiveType {
options = append(options, WithScreenShotLiveType(true))
}
if o.ScreenShotWithLivePopularity {
options = append(options, WithScreenShotLivePopularity(true))
}
if len(o.ScreenShotWithUITypes) > 0 {
options = append(options, WithScreenShotUITypes(o.ScreenShotWithUITypes...))
}
if o.ScreenShotWithClosePopups {
options = append(options, WithScreenShotClosePopups(true))
}
if o.ScreenShotWithOCRCluster != "" {
options = append(options, WithScreenOCRCluster(o.ScreenShotWithOCRCluster))
}
if o.ScreenShotFileName != "" {
options = append(options, WithScreenShotFileName(o.ScreenShotFileName))
}
return options
}
func (o *ScreenShotOptions) List() []string {
options := []string{}
if o.ScreenShotWithUpload {
options = append(options, "upload")
}
if o.ScreenShotWithOCR {
options = append(options, "ocr")
}
if o.ScreenShotWithLiveType {
options = append(options, "liveType")
}
if o.ScreenShotWithLivePopularity {
options = append(options, "livePopularity")
}
// UI detection
if len(o.ScreenShotWithUITypes) > 0 {
options = append(options, "ui")
}
if o.ScreenShotWithClosePopups {
options = append(options, "close")
}
return options
}
type ScreenShotOption func(o *ScreenShotOptions)
func WithScreenShotOCR(ocrOn bool) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithOCR = ocrOn
}
}
func WithScreenShotUpload(uploadOn bool) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithUpload = uploadOn
}
}
func WithScreenShotLiveType(liveTypeOn bool) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithLiveType = liveTypeOn
}
}
func WithScreenShotLivePopularity(livePopularityOn bool) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithLivePopularity = livePopularityOn
}
}
func WithScreenShotUITypes(uiTypes ...string) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithUITypes = uiTypes
}
}
func WithScreenShotClosePopups(closeOn bool) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithClosePopups = closeOn
}
}
func WithScreenOCRCluster(ocrCluster string) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotWithOCRCluster = ocrCluster
}
}
func WithScreenShotFileName(fileName string) ScreenShotOption {
return func(o *ScreenShotOptions) {
o.ScreenShotFileName = fileName
}
}
// (x1, y1) is the top left corner, (x2, y2) is the bottom right corner
// [x1, y1, x2, y2] in percentage of the screen
type Scope []float64
func (s Scope) ToAbs(windowSize Size) AbsScope {
x1, y1, x2, y2 := s[0], s[1], s[2], s[3]
// convert relative scope to absolute scope
absX1 := int(x1 * float64(windowSize.Width))
absY1 := int(y1 * float64(windowSize.Height))
absX2 := int(x2 * float64(windowSize.Width))
absY2 := int(y2 * float64(windowSize.Height))
return AbsScope{absX1, absY1, absX2, absY2}
}
// [x1, y1, x2, y2] in absolute pixels
type AbsScope []int
func (s AbsScope) Option() ScreenFilterOption {
return WithAbsScope(s[0], s[1], s[2], s[3])
}
func NewScreenFilterOptions(opts ...ScreenFilterOption) *ScreenFilterOptions {
options := &ScreenFilterOptions{}
for _, option := range opts {
option(options)
}
return options
}
type ScreenFilterOptions struct {
// scope related
Scope Scope `json:"scope,omitempty" yaml:"scope,omitempty"`
AbsScope AbsScope `json:"abs_scope,omitempty" yaml:"abs_scope,omitempty"`
Regex bool `json:"regex,omitempty" yaml:"regex,omitempty"` // use regex to match text
Offset []int `json:"offset,omitempty" yaml:"offset,omitempty"` // used to tap offset of point
OffsetRandomRange []int `json:"offset_random_range,omitempty" yaml:"offset_random_range,omitempty"` // set random range [min, max] for tap/swipe points
Index int `json:"index,omitempty" yaml:"index,omitempty"` // index of the target element
MatchOne bool `json:"match_one,omitempty" yaml:"match_one,omitempty"` // match one of the targets if existed
}
type ScreenFilterOption func(o *ScreenFilterOptions)
// WithScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all in [0, 1], which means the relative position of the screen
func WithScope(x1, y1, x2, y2 float64) ScreenFilterOption {
return func(o *ScreenFilterOptions) {
o.Scope = Scope{x1, y1, x2, y2}
}
}
// WithAbsScope inputs area of [(x1,y1), (x2,y2)]
// x1, y1, x2, y2 are all absolute position of the screen
func WithAbsScope(x1, y1, x2, y2 int) ScreenFilterOption {
return func(o *ScreenFilterOptions) {
o.AbsScope = AbsScope{x1, y1, x2, y2}
}
}
// tap [x, y] with offset [offsetX, offsetY]
func WithTapOffset(offsetX, offsetY int) ScreenFilterOption {
return func(o *ScreenFilterOptions) {
o.Offset = []int{offsetX, offsetY}
}
}
func WithRegex(regex bool) ScreenFilterOption {
return func(o *ScreenFilterOptions) {
o.Regex = regex
}
}
func WithMatchOne(matchOne bool) ScreenFilterOption {
return func(o *ScreenFilterOptions) {
o.MatchOne = matchOne
}
}
func WithIndex(index int) ScreenFilterOption {
return func(o *ScreenFilterOptions) {
o.Index = index
}
}
-80
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)
}
+1 -1
View File
@@ -152,7 +152,7 @@ func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *Dri
if dev.UIA2 || dev.LogOn { if dev.UIA2 || dev.LogOn {
driver, err = NewUIA2Driver(dev) driver, err = NewUIA2Driver(dev)
} else if dev.STUB { } else if dev.STUB {
driver, err = NewStubDriver(dev) driver, err = NewStubAndroidDriver(dev)
} else { } else {
driver, err = NewADBDriver(dev) driver, err = NewADBDriver(dev)
} }
+14 -19
View File
@@ -23,6 +23,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/utf7" "github.com/httprunner/httprunner/v5/internal/utf7"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
@@ -39,6 +40,7 @@ func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) {
type ADBDriver struct { type ADBDriver struct {
*AndroidDevice *AndroidDevice
*Session *Session
// DriverExt
} }
func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, err error) { 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 return
} }
func (ad *ADBDriver) GetDevice() IDevice {
return ad.AndroidDevice
}
func (ad *ADBDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) { func (ad *ADBDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
@@ -104,7 +110,7 @@ func (ad *ADBDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
return return
} }
func (ad *ADBDriver) getWindowSize() (size Size, err error) { func (ad *ADBDriver) getWindowSize() (size ai.Size, err error) {
// adb shell wm size // adb shell wm size
output, err := ad.runShellCommand("wm", "size") output, err := ad.runShellCommand("wm", "size")
if err != nil { if err != nil {
@@ -130,14 +136,14 @@ func (ad *ADBDriver) getWindowSize() (size Size, err error) {
ss := strings.Split(resolution, "x") ss := strings.Split(resolution, "x")
width, _ := strconv.Atoi(ss[0]) width, _ := strconv.Atoi(ss[0])
height, _ := strconv.Atoi(ss[1]) height, _ := strconv.Atoi(ss[1])
return Size{Width: width, Height: height}, nil return ai.Size{Width: width, Height: height}, nil
} }
} }
err = errors.New("physical window size not found by adb") err = errors.New("physical window size not found by adb")
return return
} }
func (ad *ADBDriver) WindowSize() (size Size, err error) { func (ad *ADBDriver) WindowSize() (size ai.Size, err error) {
if !ad.windowSize.IsNil() { if !ad.windowSize.IsNil() {
// use cached window size // use cached window size
return ad.windowSize, nil return ad.windowSize, nil
@@ -163,7 +169,7 @@ func (ad *ADBDriver) WindowSize() (size Size, err error) {
return size, nil return size, nil
} }
func (ad *ADBDriver) Screen() (screen Screen, err error) { func (ad *ADBDriver) Screen() (screen ai.Screen, err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
@@ -172,21 +178,6 @@ func (ad *ADBDriver) Scale() (scale float64, err error) {
return 1, nil 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. // PressBack simulates a short press on the BACK button.
func (ad *ADBDriver) PressBack(opts ...option.ActionOption) (err error) { func (ad *ADBDriver) PressBack(opts ...option.ActionOption) (err error) {
// adb shell input keyevent 4 // adb shell input keyevent 4
@@ -1088,6 +1079,10 @@ func (ad *ADBDriver) RecordScreen(folderPath string, duration time.Duration) (vi
return filepath.Abs(fileName) return filepath.Abs(fileName)
} }
func (ad *ADBDriver) Setup() error {
return nil
}
func (ad *ADBDriver) TearDown() error { func (ad *ADBDriver) TearDown() error {
return nil return nil
} }
+1 -1
View File
@@ -22,7 +22,7 @@ const (
DouyinServerPort = 32316 DouyinServerPort = 32316
) )
func NewStubDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) { func NewStubAndroidDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) {
socketLocalPort, err := device.Forward(StubSocketName) socketLocalPort, err := device.Forward(StubSocketName)
if err != nil { if err != nil {
return nil, errors.Wrap(code.DeviceConnectionError, return nil, errors.Wrap(code.DeviceConnectionError,
+1 -1
View File
@@ -14,7 +14,7 @@ func setupStubDriver(t *testing.T) {
device, err := NewAndroidDevice() device, err := NewAndroidDevice()
checkErr(t, err) checkErr(t, err)
device.STUB = true device.STUB = true
androidStubDriver, err = NewStubDriver(device) androidStubDriver, err = NewStubAndroidDriver(device)
checkErr(t, err) checkErr(t, err)
} }
+5 -4
View File
@@ -16,6 +16,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/utf7" "github.com/httprunner/httprunner/v5/internal/utf7"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
@@ -182,7 +183,7 @@ func (ud *UIA2Driver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
return 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")) // register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size"))
if !ud.windowSize.IsNil() { if !ud.windowSize.IsNil() {
// use cached window size // use cached window size
@@ -191,11 +192,11 @@ func (ud *UIA2Driver) WindowSize() (size Size, err error) {
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = ud.httpGET("/session", ud.sessionID, "window/:windowHandle/size"); err != nil { if rawResp, err = ud.httpGET("/session", ud.sessionID, "window/:windowHandle/size"); err != nil {
return 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 { 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 size = reply.Value.Size
+5 -6
View File
@@ -6,17 +6,16 @@ import (
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice // current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
type IDevice interface { type IDevice interface {
UUID() string // ios udid or android serial
Setup() error Setup() error
Teardown() 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 Install(appPath string, opts ...option.InstallOption) error
Uninstall(packageName string) error Uninstall(packageName string) error
GetPackageInfo(packageName string) (AppInfo, error) GetPackageInfo(packageName string) (AppInfo, error)
// TODO: remove?
LogEnabled() bool
} }
+48 -16
View File
@@ -16,9 +16,17 @@ import (
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "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 // current implemeted driver: ADBDriver, UIA2Driver, WDADriver, HDCDriver
type IDriver interface { type IDriver interface {
// NewSession starts a new session and returns the DriverSession. // NewSession starts a new session and returns the DriverSession.
@@ -34,6 +42,7 @@ type IDriver interface {
Status() (DeviceStatus, error) Status() (DeviceStatus, error)
GetDevice() IDevice
DeviceInfo() (DeviceInfo, error) DeviceInfo() (DeviceInfo, error)
// Location Returns device location data. // Location Returns device location data.
@@ -53,13 +62,10 @@ type IDriver interface {
// WindowSize Return the width and height in portrait mode. // WindowSize Return the width and height in portrait mode.
// when getting the window size in wda/ui2/adb, if the device is in landscape mode, // when getting the window size in wda/ui2/adb, if the device is in landscape mode,
// the width and height will be reversed. // the width and height will be reversed.
WindowSize() (Size, error) WindowSize() (ai.Size, error)
Screen() (Screen, error) Screen() (ai.Screen, error)
Scale() (float64, 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 Forces the device under test to switch to the home screen
Homescreen() error Homescreen() error
@@ -162,40 +168,66 @@ type IDriver interface {
GetDriverResults() []*DriverRequests GetDriverResults() []*DriverRequests
RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error) RecordScreen(folderPath string, duration time.Duration) (videoPath string, err error)
Setup() error
TearDown() 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 { type DriverExt struct {
Device IDevice
Driver IDriver 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) { func newDriverExt(device IDevice, driver IDriver, opts ...option.DriverOption) (dExt *DriverExt, err error) {
options := option.NewDriverOptions(opts...) options := option.NewDriverOptions(opts...)
dExt = &DriverExt{ dExt = &DriverExt{
Device: device,
Driver: driver, Driver: driver,
} }
if options.WithImageService { if options.WithImageService {
if dExt.ImageService, err = newVEDEMImageService(); err != nil { if dExt.ImageService, err = ai.NewVEDEMImageService(); err != nil {
return nil, err return nil, err
} }
} }
if options.WithResultFolder { 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 return dExt, nil
} }
func (dExt *DriverExt) GetDriver() IDriver {
return dExt.Driver
}
func (dExt *DriverExt) Setup() error { func (dExt *DriverExt) Setup() error {
// unlock device screen // unlock device screen
err := dExt.Driver.Unlock() err := dExt.Driver.Unlock()
+3 -3
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 { 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{}) stopChan := make(chan struct{})
go func() { go func() {
ticker := time.NewTicker(5 * time.Second) 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 { func (dExt *DriverExt) Uninstall(packageName string, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...) actionOptions := option.NewActionOptions(opts...)
err := dExt.Device.Uninstall(packageName) err := dExt.Driver.GetDevice().Uninstall(packageName)
if err != nil { if err != nil {
log.Warn().Err(err).Msg("failed to uninstall") log.Warn().Err(err).Msg("failed to uninstall")
} }
+8 -7
View File
@@ -5,6 +5,7 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "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 { for _, popup := range popups {
if len(popup) != 2 { if len(popup) != 2 {
continue continue
@@ -42,7 +43,7 @@ func findTextPopup(screenTexts OCRTexts) (closePoint *OCRText) {
return return
} }
func (dExt *DriverExt) handleTextPopup(screenTexts OCRTexts) error { func (dExt *DriverExt) handleTextPopup(screenTexts ai.OCRTexts) error {
closePoint := findTextPopup(screenTexts) closePoint := findTextPopup(screenTexts)
if closePoint == nil { if closePoint == nil {
// no popup found // no popup found
@@ -76,13 +77,13 @@ func (dExt *DriverExt) AutoPopupHandler() error {
} }
type PopupInfo struct { type PopupInfo struct {
*ClosePopupsResult *ai.ClosePopupsResult
ClosePoints []PointF `json:"close_points,omitempty"` // CV 识别的所有关闭按钮(仅关闭按钮,可能存在多个) ClosePoints []ai.PointF `json:"close_points,omitempty"` // CV 识别的所有关闭按钮(仅关闭按钮,可能存在多个)
PicName string `json:"pic_name"` PicName string `json:"pic_name"`
PicURL string `json:"pic_url"` PicURL string `json:"pic_url"`
} }
func (p *PopupInfo) ClosePoint() *PointF { func (p *PopupInfo) ClosePoint() *ai.PointF {
closeResult := p.ClosePopupsResult closeResult := p.ClosePopupsResult
if closeResult == nil { if closeResult == nil {
return nil return nil
+18 -20
View File
@@ -18,21 +18,22 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/config" "github.com/httprunner/httprunner/v5/internal/config"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
type ScreenResult struct { type ScreenResult struct {
bufSource *bytes.Buffer // raw image buffer bytes bufSource *bytes.Buffer // raw image buffer bytes
ImagePath string `json:"image_path"` // image file path ImagePath string `json:"image_path"` // image file path
Resolution Size `json:"resolution"` Resolution ai.Size `json:"resolution"`
UploadedURL string `json:"uploaded_url"` // uploaded image url UploadedURL string `json:"uploaded_url"` // uploaded image url
Texts OCRTexts `json:"texts"` // dumped raw OCRTexts Texts ai.OCRTexts `json:"texts"` // dumped raw OCRTexts
Icons UIResultMap `json:"icons"` // CV 识别的图标 Icons ai.UIResultMap `json:"icons"` // CV 识别的图标
Tags []string `json:"tags"` // tags for image, e.g. ["feed", "ad", "live"] Tags []string `json:"tags"` // tags for image, e.g. ["feed", "ad", "live"]
Popup *PopupInfo `json:"popup,omitempty"` 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 { if x1 > 1 || y1 > 1 || x2 > 1 || y2 > 1 {
log.Warn().Msg("x1, y1, x2, y2 should be in percentage, skip filter scope") log.Warn().Msg("x1, y1, x2, y2 should be in percentage, skip filter scope")
return s.Texts 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 // GetScreenResult takes a screenshot, returns the image recognition result
func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) { func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error) {
actionOptions := option.NewActionOptions(opts...) actionOptions := option.NewActionOptions(opts...)
if actionOptions.MaxRetryTimes == 0 {
actionOptions.MaxRetryTimes = 1
}
var fileName string var fileName string
screenshotActions := actionOptions.ScreenshotActions() screenshotActions := actionOptions.ScreenshotActions()
@@ -61,9 +59,9 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
} }
var bufSource *bytes.Buffer var bufSource *bytes.Buffer
var imageResult *ImageResult var imageResult *ai.ImageResult
var imagePath string var imagePath string
var windowSize Size var windowSize ai.Size
var lastErr error var lastErr error
// get screenshot info with retry // get screenshot info with retry
@@ -87,9 +85,9 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
Tags: nil, Tags: nil,
Resolution: windowSize, Resolution: windowSize,
} }
imageResult, err = dExt.ImageService.GetImage(bufSource, opts...) imageResult, err = dExt.ImageService.GetImageFromBuffer(bufSource, opts...)
if err != nil { if err != nil {
log.Error().Err(err).Msg("GetImage from ImageService failed") log.Error().Err(err).Msg("GetImageFromBuffer from ImageService failed")
lastErr = err lastErr = err
continue continue
} }
@@ -130,7 +128,7 @@ func (dExt *DriverExt) GetScreenResult(opts ...option.ActionOption) (screenResul
return screenResult, nil 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...) actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" { if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName("get_screen_texts")) 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 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 // find text using OCR
if !builtin.IsPathExists(search) { if !builtin.IsPathExists(search) {
return dExt.FindScreenText(search, opts...) return dExt.FindScreenText(search, opts...)
@@ -153,7 +151,7 @@ func (dExt *DriverExt) FindUIRectInUIKit(search string, opts ...option.ActionOpt
return 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...) actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" { if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text))) opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("find_screen_text_%s", text)))
@@ -175,7 +173,7 @@ func (dExt *DriverExt) FindScreenText(text string, opts ...option.ActionOption)
return 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...) actionOptions := option.NewActionOptions(opts...)
if actionOptions.ScreenShotFileName == "" { if actionOptions.ScreenShotFileName == "" {
opts = append(opts, option.WithScreenShotFileName( opts = append(opts, option.WithScreenShotFileName(
+2 -1
View File
@@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@@ -24,7 +25,7 @@ type Session struct {
// cache to avoid repeated query // cache to avoid repeated query
scale float64 scale float64
windowSize Size windowSize ai.Size
// cache uia2/wda request and response // cache uia2/wda request and response
requests []*DriverRequests requests []*DriverRequests
+2 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "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 := option.NewActionOptions(opts...)
actionOptions.Identifier = "" actionOptions.Identifier = ""
optionsWithoutIdentifier := actionOptions.Options() optionsWithoutIdentifier := actionOptions.Options()
var point PointF var point ai.PointF
findTexts := func(d *DriverExt) error { findTexts := func(d *DriverExt) error {
var err error var err error
screenResult, err := d.GetScreenResult( screenResult, err := d.GetScreenResult(
+29
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)
}
-25
View File
@@ -98,31 +98,6 @@ func (dev *HarmonyDevice) LogEnabled() bool {
return dev.LogOn 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 { func (dev *HarmonyDevice) Install(appPath string, opts ...option.InstallOption) error {
return nil return nil
} }
+71 -64
View File
@@ -10,12 +10,15 @@ import (
"code.byted.org/iesqa/ghdc" "code.byted.org/iesqa/ghdc"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
type hdcDriver struct { type HDCDriver struct {
*HarmonyDevice *HarmonyDevice
*Session *Session
IDriver
*DriverExt
points []ExportPoint points []ExportPoint
uiDriver *ghdc.UIDriver uiDriver *ghdc.UIDriver
} }
@@ -28,10 +31,10 @@ const (
POWER_STATUS_ON PowerStatus = "POWER_STATUS_ON" POWER_STATUS_ON PowerStatus = "POWER_STATUS_ON"
) )
func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) { func NewHDCDriver(device *HarmonyDevice) (driver *HDCDriver, err error) {
driver = new(hdcDriver) driver = new(HDCDriver)
driver.Device = device driver.HarmonyDevice = device
uiDriver, err := ghdc.NewUIDriver(*device) uiDriver, err := ghdc.NewUIDriver(*device.Device)
if err != nil { if err != nil {
log.Error().Err(err).Msg("failed to new harmony ui driver") log.Error().Err(err).Msg("failed to new harmony ui driver")
return nil, err return nil, err
@@ -41,64 +44,64 @@ func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) {
return return
} }
func (hd *hdcDriver) NewSession(capabilities option.Capabilities) (Session, error) { func (hd *HDCDriver) NewSession(capabilities option.Capabilities) (Session, error) {
hd.Reset() hd.Reset()
hd.Unlock() hd.Unlock()
return Session{}, errDriverNotImplemented return Session{}, errDriverNotImplemented
} }
func (hd *hdcDriver) DeleteSession() error { func (hd *HDCDriver) DeleteSession() error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) GetSession() *Session { func (hd *HDCDriver) GetSession() *Session {
return hd.Session return hd.Session
} }
func (hd *hdcDriver) Status() (DeviceStatus, error) { func (hd *HDCDriver) Status() (DeviceStatus, error) {
return DeviceStatus{}, errDriverNotImplemented 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 return DeviceInfo{}, errDriverNotImplemented
} }
func (hd *hdcDriver) Location() (Location, error) { func (hd *HDCDriver) Location() (Location, error) {
return Location{}, errDriverNotImplemented return Location{}, errDriverNotImplemented
} }
func (hd *hdcDriver) BatteryInfo() (BatteryInfo, error) { func (hd *HDCDriver) BatteryInfo() (BatteryInfo, error) {
return BatteryInfo{}, errDriverNotImplemented 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() display, err := hd.uiDriver.GetDisplaySize()
if err != nil { if err != nil {
log.Error().Err(err).Msg("failed to get window size") log.Error().Err(err).Msg("failed to get window size")
return Size{}, err return ai.Size{}, err
} }
size.Width = display.Width size.Width = display.Width
size.Height = display.Height size.Height = display.Height
return size, err return size, err
} }
func (hd *hdcDriver) Screen() (Screen, error) { func (hd *HDCDriver) Screen() (ai.Screen, error) {
return Screen{}, errDriverNotImplemented return ai.Screen{}, errDriverNotImplemented
} }
func (hd *hdcDriver) Scale() (float64, error) { func (hd *HDCDriver) Scale() (float64, error) {
return 1, nil return 1, nil
} }
func (hd *hdcDriver) GetTimestamp() (timestamp int64, err error) { func (hd *HDCDriver) Homescreen() error {
return 0, errDriverNotImplemented
}
func (hd *hdcDriver) Homescreen() error {
return hd.uiDriver.PressKey(ghdc.KEYCODE_HOME) 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 // Todo 检查是否锁屏 hdc shell hidumper -s RenderService -a screen
screenInfo, err := hd.RunShellCommand("hidumper", "-s", "RenderService", "-a", "screen") screenInfo, err := hd.RunShellCommand("hidumper", "-s", "RenderService", "-a", "screen")
if err != nil { if err != nil {
@@ -120,12 +123,12 @@ func (hd *hdcDriver) Unlock() (err error) {
return hd.Swipe(500, 1500, 500, 500) return hd.Swipe(500, 1500, 500, 500)
} }
func (hd *hdcDriver) AppLaunch(packageName string) error { func (hd *HDCDriver) AppLaunch(packageName string) error {
// Todo // Todo
return errDriverNotImplemented 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) _, err := hd.RunShellCommand("aa", "force-stop", packageName)
if err != nil { if err != nil {
log.Error().Err(err).Msg("failed to terminal app") log.Error().Err(err).Msg("failed to terminal app")
@@ -134,29 +137,29 @@ func (hd *hdcDriver) AppTerminate(packageName string) (bool, error) {
return true, nil return true, nil
} }
func (hd *hdcDriver) GetForegroundApp() (app AppInfo, err error) { func (hd *HDCDriver) GetForegroundApp() (app AppInfo, err error) {
// Todo // Todo
return AppInfo{}, errDriverNotImplemented return AppInfo{}, errDriverNotImplemented
} }
func (hd *hdcDriver) AssertForegroundApp(packageName string, activityType ...string) error { func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...string) error {
// Todo // Todo
return nil return nil
} }
func (hd *hdcDriver) StartCamera() error { func (hd *HDCDriver) StartCamera() error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) StopCamera() error { func (hd *HDCDriver) StopCamera() error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) Orientation() (orientation Orientation, err error) { func (hd *HDCDriver) Orientation() (orientation Orientation, err error) {
return OrientationPortrait, nil 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...) actionOptions := option.NewActionOptions(opts...)
if len(actionOptions.Offset) == 2 { 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)) 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 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 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 return errDriverNotImplemented
} }
// Swipe works like Drag, but `pressForDuration` value is 0 // 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...) actionOptions := option.NewActionOptions(opts...)
if len(actionOptions.Offset) == 4 { if len(actionOptions.Offset) == 4 {
fromX += float64(actionOptions.Offset[0]) 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)) 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 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 return nil, errDriverNotImplemented
} }
func (hd *hdcDriver) SetIme(ime string) error { func (hd *HDCDriver) SetIme(ime string) error {
return errDriverNotImplemented 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) 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) return hd.uiDriver.InputText(text)
} }
func (hd *hdcDriver) Clear(packageName string) error { func (hd *HDCDriver) Clear(packageName string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) PressButton(devBtn DeviceButton) error { func (hd *HDCDriver) PressButton(devBtn DeviceButton) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) PressBack(opts ...option.ActionOption) error { func (hd *HDCDriver) PressBack(opts ...option.ActionOption) error {
return hd.uiDriver.PressBack() 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 return nil
} }
func (hd *hdcDriver) PressKeyCode(keyCode KeyCode) (err error) { func (hd *HDCDriver) PressKeyCode(keyCode KeyCode) (err error) {
return errDriverNotImplemented 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) return hd.uiDriver.PressKey(keyCode)
} }
func (hd *hdcDriver) Screenshot() (*bytes.Buffer, error) { func (hd *HDCDriver) Screenshot() (*bytes.Buffer, error) {
tempDir := os.TempDir() tempDir := os.TempDir()
screenshotPath := fmt.Sprintf("%s/screenshot_%d.png", tempDir, time.Now().Unix()) screenshotPath := fmt.Sprintf("%s/screenshot_%d.png", tempDir, time.Now().Unix())
err := hd.uiDriver.Screenshot(screenshotPath) err := hd.uiDriver.Screenshot(screenshotPath)
@@ -274,74 +277,78 @@ func (hd *hdcDriver) Screenshot() (*bytes.Buffer, error) {
return bytes.NewBuffer(raw), nil 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 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 err = errDriverNotImplemented
return return
} }
func (hd *hdcDriver) LogoutNoneUI(packageName string) error { func (hd *HDCDriver) LogoutNoneUI(packageName string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) TapByText(text string, opts ...option.ActionOption) error { func (hd *HDCDriver) TapByText(text string, opts ...option.ActionOption) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) TapByTexts(actions ...TapTextAction) error { func (hd *HDCDriver) TapByTexts(actions ...TapTextAction) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) AccessibleSource() (string, error) { func (hd *HDCDriver) AccessibleSource() (string, error) {
return "", errDriverNotImplemented return "", errDriverNotImplemented
} }
func (hd *hdcDriver) HealthCheck() error { func (hd *HDCDriver) HealthCheck() error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) GetAppiumSettings() (map[string]interface{}, error) { func (hd *HDCDriver) GetAppiumSettings() (map[string]interface{}, error) {
return nil, errDriverNotImplemented 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 return nil, errDriverNotImplemented
} }
func (hd *hdcDriver) IsHealthy() (bool, error) { func (hd *HDCDriver) IsHealthy() (bool, error) {
return false, errDriverNotImplemented return false, errDriverNotImplemented
} }
func (hd *hdcDriver) StartCaptureLog(identifier ...string) (err error) { func (hd *HDCDriver) StartCaptureLog(identifier ...string) (err error) {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (hd *hdcDriver) StopCaptureLog() (result interface{}, err error) { func (hd *HDCDriver) StopCaptureLog() (result interface{}, err error) {
// defer clear(hd.points) // defer clear(hd.points)
return hd.points, nil return hd.points, nil
} }
func (hd *hdcDriver) GetDriverResults() []*DriverRequests { func (hd *HDCDriver) GetDriverResults() []*DriverRequests {
return nil 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 return "", nil
} }
func (hd *hdcDriver) TearDown() error { func (hd *HDCDriver) Setup() error {
return nil 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 err = errDriverNotImplemented
return return
} }
func (hd *hdcDriver) SetRotation(rotation Rotation) (err error) { func (hd *HDCDriver) SetRotation(rotation Rotation) (err error) {
err = errDriverNotImplemented err = errDriverNotImplemented
return return
} }
+5 -9
View File
@@ -7,18 +7,14 @@ import (
"testing" "testing"
) )
var harmonyDriverExt *DriverExt var hdcDriver *HDCDriver
func setupHarmonyDevice(t *testing.T) { func setupHarmonyDevice(t *testing.T) {
device, err := NewHarmonyDevice() device, err := NewHarmonyDevice()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
driver, err = device.NewUSBDriver() hdcDriver, err = NewHDCDriver(device)
if err != nil {
t.Fatal(err)
}
harmonyDriverExt, err = newDriverExt(device, driver)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -35,7 +31,7 @@ func TestWindowSize(t *testing.T) {
func TestHarmonyTap(t *testing.T) { func TestHarmonyTap(t *testing.T) {
setupHarmonyDevice(t) setupHarmonyDevice(t)
err := harmonyDriverExt.TapAbsXY(200, 2000) err := hdcDriver.TapAbsXY(200, 2000)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -43,7 +39,7 @@ func TestHarmonyTap(t *testing.T) {
func TestHarmonySwipe(t *testing.T) { func TestHarmonySwipe(t *testing.T) {
setupHarmonyDevice(t) setupHarmonyDevice(t)
err := harmonyDriverExt.SwipeLeft() err := hdcDriver.SwipeLeft()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -51,7 +47,7 @@ func TestHarmonySwipe(t *testing.T) {
func TestHarmonyInput(t *testing.T) { func TestHarmonyInput(t *testing.T) {
setupHarmonyDevice(t) setupHarmonyDevice(t)
err := harmonyDriverExt.Input("test") err := hdcDriver.Input("test")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+1 -1
View File
@@ -552,7 +552,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID
Int("localPort", localPort).Int("localMjpegPort", localMjpegPort). Int("localPort", localPort).Int("localMjpegPort", localMjpegPort).
Msg("init WDA HTTP driver") Msg("init WDA HTTP driver")
wd := new(wdaDriver) wd := new(WDADriver)
wd.IOSDevice = dev wd.IOSDevice = dev
wd.udid = dev.UDID wd.udid = dev.UDID
wd.client = &http.Client{ wd.client = &http.Client{
+49 -66
View File
@@ -9,11 +9,12 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "github.com/httprunner/httprunner/v5/pkg/uixt/option"
) )
type stubIOSDriver struct { type stubIOSDriver struct {
*wdaDriver *WDADriver
bightInsightPrefix string bightInsightPrefix string
serverPrefix string serverPrefix string
@@ -38,7 +39,7 @@ func newStubIOSDriver(bightInsightAddr, serverAddr string, dev *IOSDevice, readT
} }
func (s *stubIOSDriver) setUpWda() (err error) { func (s *stubIOSDriver) setUpWda() (err error) {
if s.wdaDriver == nil { if s.WDADriver == nil {
capabilities := option.NewCapabilities() capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept) capabilities.WithDefaultAlertAction(option.AlertActionAccept)
driver, err := s.device.NewHTTPDriver(capabilities) 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") log.Error().Err(err).Msg("stub driver failed to init wda driver")
return err return err
} }
s.wdaDriver = driver.(*wdaDriver) s.WDADriver = driver.(*WDADriver)
} }
return nil return nil
} }
@@ -57,7 +58,7 @@ func (s *stubIOSDriver) NewSession(capabilities option.Capabilities) (Session, e
if err != nil { if err != nil {
return Session{}, err return Session{}, err
} }
return s.wdaDriver.NewSession(capabilities) return s.WDADriver.NewSession(capabilities)
} }
// DeleteSession Kills application associated with that session and removes session // DeleteSession Kills application associated with that session and removes session
@@ -68,7 +69,7 @@ func (s *stubIOSDriver) DeleteSession() error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.DeleteSession() return s.WDADriver.DeleteSession()
} }
func (s *stubIOSDriver) Status() (DeviceStatus, error) { func (s *stubIOSDriver) Status() (DeviceStatus, error) {
@@ -76,7 +77,7 @@ func (s *stubIOSDriver) Status() (DeviceStatus, error) {
if err != nil { if err != nil {
return DeviceStatus{}, err return DeviceStatus{}, err
} }
return s.wdaDriver.Status() return s.WDADriver.Status()
} }
func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) { func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) {
@@ -84,7 +85,7 @@ func (s *stubIOSDriver) DeviceInfo() (DeviceInfo, error) {
if err != nil { if err != nil {
return DeviceInfo{}, err return DeviceInfo{}, err
} }
return s.wdaDriver.DeviceInfo() return s.WDADriver.DeviceInfo()
} }
func (s *stubIOSDriver) Location() (Location, error) { func (s *stubIOSDriver) Location() (Location, error) {
@@ -92,7 +93,7 @@ func (s *stubIOSDriver) Location() (Location, error) {
if err != nil { if err != nil {
return Location{}, err return Location{}, err
} }
return s.wdaDriver.Location() return s.WDADriver.Location()
} }
func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) { func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) {
@@ -100,26 +101,26 @@ func (s *stubIOSDriver) BatteryInfo() (BatteryInfo, error) {
if err != nil { if err != nil {
return BatteryInfo{}, err return BatteryInfo{}, err
} }
return s.wdaDriver.BatteryInfo() return s.WDADriver.BatteryInfo()
} }
// WindowSize Return the width and height in portrait mode. // WindowSize Return the width and height in portrait mode.
// when getting the window size in wda/ui2/adb, if the device is in landscape mode, // when getting the window size in wda/ui2/adb, if the device is in landscape mode,
// the width and height will be reversed. // the width and height will be reversed.
func (s *stubIOSDriver) WindowSize() (Size, error) { func (s *stubIOSDriver) WindowSize() (ai.Size, error) {
err := s.setUpWda() err := s.setUpWda()
if err != nil { 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() err := s.setUpWda()
if err != nil { if err != nil {
return Screen{}, err return ai.Screen{}, err
} }
return s.wdaDriver.Screen() return s.WDADriver.Screen()
} }
func (s *stubIOSDriver) Scale() (float64, error) { func (s *stubIOSDriver) Scale() (float64, error) {
@@ -127,16 +128,7 @@ func (s *stubIOSDriver) Scale() (float64, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
return s.wdaDriver.Scale() 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()
} }
// Homescreen Forces the device under test to switch to the home screen // Homescreen Forces the device under test to switch to the home screen
@@ -145,7 +137,7 @@ func (s *stubIOSDriver) Homescreen() error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.Homescreen() return s.WDADriver.Homescreen()
} }
func (s *stubIOSDriver) Unlock() (err error) { func (s *stubIOSDriver) Unlock() (err error) {
@@ -153,7 +145,7 @@ func (s *stubIOSDriver) Unlock() (err error) {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.Unlock() return s.WDADriver.Unlock()
} }
// AppLaunch Launch an application with given bundle identifier in scope of current session. // AppLaunch Launch an application with given bundle identifier in scope of current session.
@@ -163,7 +155,7 @@ func (s *stubIOSDriver) AppLaunch(packageName string) error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.AppLaunch(packageName) return s.WDADriver.AppLaunch(packageName)
} }
// AppTerminate Terminate an application with the given package name. // AppTerminate Terminate an application with the given package name.
@@ -173,7 +165,7 @@ func (s *stubIOSDriver) AppTerminate(packageName string) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return s.wdaDriver.AppTerminate(packageName) return s.WDADriver.AppTerminate(packageName)
} }
// GetForegroundApp returns current foreground app package name and activity name // 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 { if err != nil {
return AppInfo{}, err return AppInfo{}, err
} }
return s.wdaDriver.GetForegroundApp() 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...)
} }
// StartCamera Starts a new camera for recording // StartCamera Starts a new camera for recording
@@ -200,7 +183,7 @@ func (s *stubIOSDriver) StartCamera() error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.StartCamera() return s.WDADriver.StartCamera()
} }
// StopCamera Stops the camera for recording // StopCamera Stops the camera for recording
@@ -209,7 +192,7 @@ func (s *stubIOSDriver) StopCamera() error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.StopCamera() return s.WDADriver.StopCamera()
} }
func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) { func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) {
@@ -217,7 +200,7 @@ func (s *stubIOSDriver) Orientation() (orientation Orientation, err error) {
if err != nil { if err != nil {
return OrientationPortrait, err return OrientationPortrait, err
} }
return s.wdaDriver.Orientation() return s.WDADriver.Orientation()
} }
// Tap Sends a tap event at the coordinate. // Tap Sends a tap event at the coordinate.
@@ -226,7 +209,7 @@ func (s *stubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error {
if err != nil { if err != nil {
return err 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. // 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 { if err != nil {
return err 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. // 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 { if err != nil {
return err 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. // 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 { if err != nil {
return err 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 // SetPasteboard Sets data to the general pasteboard
@@ -265,7 +248,7 @@ func (s *stubIOSDriver) SetPasteboard(contentType PasteboardType, content string
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.SetPasteboard(contentType, content) return s.WDADriver.SetPasteboard(contentType, content)
} }
// GetPasteboard Gets the data contained in the general pasteboard. // 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 { if err != nil {
return nil, err return nil, err
} }
return s.wdaDriver.GetPasteboard(contentType) return s.WDADriver.GetPasteboard(contentType)
} }
func (s *stubIOSDriver) SetIme(ime string) error { func (s *stubIOSDriver) SetIme(ime string) error {
@@ -284,7 +267,7 @@ func (s *stubIOSDriver) SetIme(ime string) error {
if err != nil { if err != nil {
return err 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, // 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 { if err != nil {
return err return err
} }
return s.wdaDriver.SendKeys(text, opts...) return s.WDADriver.SendKeys(text, opts...)
} }
// Input works like SendKeys // Input works like SendKeys
@@ -304,7 +287,7 @@ func (s *stubIOSDriver) Input(text string, opts ...option.ActionOption) error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.Input(text, opts...) return s.WDADriver.Input(text, opts...)
} }
func (s *stubIOSDriver) Clear(packageName string) error { func (s *stubIOSDriver) Clear(packageName string) error {
@@ -312,7 +295,7 @@ func (s *stubIOSDriver) Clear(packageName string) error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.Clear(packageName) return s.WDADriver.Clear(packageName)
} }
// PressButton Presses the corresponding hardware button on the device // PressButton Presses the corresponding hardware button on the device
@@ -321,7 +304,7 @@ func (s *stubIOSDriver) PressButton(devBtn DeviceButton) error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.PressButton(devBtn) return s.WDADriver.PressButton(devBtn)
} }
// PressBack Presses the back button // PressBack Presses the back button
@@ -330,7 +313,7 @@ func (s *stubIOSDriver) PressBack(opts ...option.ActionOption) error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.PressBack(opts...) return s.WDADriver.PressBack(opts...)
} }
func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
@@ -338,7 +321,7 @@ func (s *stubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.PressKeyCode(keyCode) return s.WDADriver.PressKeyCode(keyCode)
} }
func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) { func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) {
@@ -346,7 +329,7 @@ func (s *stubIOSDriver) Screenshot() (*bytes.Buffer, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s.wdaDriver.Screenshot() return s.WDADriver.Screenshot()
//screenshotService, err := instruments.NewScreenshotService(s.device.d) //screenshotService, err := instruments.NewScreenshotService(s.device.d)
//if err != nil { //if err != nil {
// log.Error().Err(err).Msg("Starting screenshot service failed") // 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 { if err != nil {
return err return err
} }
return s.wdaDriver.TapByText(text, opts...) return s.WDADriver.TapByText(text, opts...)
} }
func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error { func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error {
@@ -375,7 +358,7 @@ func (s *stubIOSDriver) TapByTexts(actions ...TapTextAction) error {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.TapByTexts(actions...) return s.WDADriver.TapByTexts(actions...)
} }
// AccessibleSource Return application elements accessibility tree // AccessibleSource Return application elements accessibility tree
@@ -384,7 +367,7 @@ func (s *stubIOSDriver) AccessibleSource() (string, error) {
if err != nil { if err != nil {
return "", err 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 // 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 { if err != nil {
return err return err
} }
return s.wdaDriver.HealthCheck() return s.WDADriver.HealthCheck()
} }
func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
@@ -405,7 +388,7 @@ func (s *stubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s.wdaDriver.GetAppiumSettings() return s.WDADriver.GetAppiumSettings()
} }
func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) { func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) {
@@ -413,7 +396,7 @@ func (s *stubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s.wdaDriver.SetAppiumSettings(settings) return s.WDADriver.SetAppiumSettings(settings)
} }
func (s *stubIOSDriver) IsHealthy() (bool, error) { func (s *stubIOSDriver) IsHealthy() (bool, error) {
@@ -421,7 +404,7 @@ func (s *stubIOSDriver) IsHealthy() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
return s.wdaDriver.IsHealthy() return s.WDADriver.IsHealthy()
} }
// triggers the log capture and returns the log entries // triggers the log capture and returns the log entries
@@ -430,7 +413,7 @@ func (s *stubIOSDriver) StartCaptureLog(identifier ...string) (err error) {
if err != nil { if err != nil {
return err return err
} }
return s.wdaDriver.StartCaptureLog(identifier...) return s.WDADriver.StartCaptureLog(identifier...)
} }
func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) { func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) {
@@ -438,7 +421,7 @@ func (s *stubIOSDriver) StopCaptureLog() (result interface{}, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s.wdaDriver.StopCaptureLog() return s.WDADriver.StopCaptureLog()
} }
func (s *stubIOSDriver) GetDriverResults() []*DriverRequests { func (s *stubIOSDriver) GetDriverResults() []*DriverRequests {
@@ -446,7 +429,7 @@ func (s *stubIOSDriver) GetDriverResults() []*DriverRequests {
if err != nil { if err != nil {
return nil return nil
} }
return s.wdaDriver.GetDriverResults() return s.WDADriver.GetDriverResults()
} }
func (s *stubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) { func (s *stubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) {
+103 -88
View File
@@ -23,19 +23,31 @@ import (
"github.com/httprunner/httprunner/v5/code" "github.com/httprunner/httprunner/v5/code"
"github.com/httprunner/httprunner/v5/internal/builtin" "github.com/httprunner/httprunner/v5/internal/builtin"
"github.com/httprunner/httprunner/v5/internal/json" "github.com/httprunner/httprunner/v5/internal/json"
"github.com/httprunner/httprunner/v5/pkg/ai"
"github.com/httprunner/httprunner/v5/pkg/uixt/option" "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 *IOSDevice
*Session *Session
DriverExt
udid string udid string
mjpegHTTPConn net.Conn // via HTTP mjpegHTTPConn net.Conn // via HTTP
mjpegClient *http.Client mjpegClient *http.Client
mjpegUrl string mjpegUrl string
} }
func (wd *wdaDriver) resetSession() error { func (wd *WDADriver) resetSession() error {
capabilities := option.NewCapabilities() capabilities := option.NewCapabilities()
capabilities.WithDefaultAlertAction(option.AlertActionAccept) capabilities.WithDefaultAlertAction(option.AlertActionAccept)
@@ -59,7 +71,7 @@ func (wd *wdaDriver) resetSession() error {
return nil 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 retryInterval := 3 * time.Second
for retryCount := 1; retryCount <= 3; retryCount++ { for retryCount := 1; retryCount <= 3; retryCount++ {
rawResp, err = wd.Request(method, rawURL, rawBody) rawResp, err = wd.Request(method, rawURL, rawBody)
@@ -93,11 +105,11 @@ func (wd *wdaDriver) httpRequest(method string, rawURL string, rawBody []byte) (
return 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) 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 var bsJSON []byte = nil
if data != nil { if data != nil {
if bsJSON, err = json.Marshal(data); err != 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) 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) 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 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:)] // [[FBRoute POST:@"/session"].withoutSession respondWithTarget:self action:@selector(handleCreateSession:)]
data := make(map[string]interface{}) data := make(map[string]interface{})
if len(capabilities) == 0 { if len(capabilities) == 0 {
@@ -136,7 +148,7 @@ func (wd *wdaDriver) NewSession(capabilities option.Capabilities) (sessionInfo S
return return
} }
func (wd *wdaDriver) DeleteSession() (err error) { func (wd *WDADriver) DeleteSession() (err error) {
if wd.mjpegClient != nil { if wd.mjpegClient != nil {
wd.mjpegClient.CloseIdleConnections() wd.mjpegClient.CloseIdleConnections()
} }
@@ -149,7 +161,7 @@ func (wd *wdaDriver) DeleteSession() (err error) {
return 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:)] // [[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)]
var rawResp rawResponse var rawResp rawResponse
// Notice: use Driver.GET instead of httpGET to avoid loop calling // Notice: use Driver.GET instead of httpGET to avoid loop calling
@@ -164,7 +176,11 @@ func (wd *wdaDriver) Status() (deviceStatus DeviceStatus, err error) {
return 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"] respondWithTarget:self action:@selector(handleGetDeviceInfo:)]
// [[FBRoute GET:@"/wda/device/info"].withoutSession // [[FBRoute GET:@"/wda/device/info"].withoutSession
var rawResp rawResponse var rawResp rawResponse
@@ -179,7 +195,7 @@ func (wd *wdaDriver) DeviceInfo() (deviceInfo DeviceInfo, err error) {
return 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"] respondWithTarget:self action:@selector(handleGetLocation:)]
// [[FBRoute GET:@"/wda/device/location"].withoutSession // [[FBRoute GET:@"/wda/device/location"].withoutSession
var rawResp rawResponse var rawResp rawResponse
@@ -194,7 +210,7 @@ func (wd *wdaDriver) Location() (location Location, err error) {
return 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:)] // [[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/batteryInfo"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/batteryInfo"); err != nil {
@@ -208,7 +224,7 @@ func (wd *wdaDriver) BatteryInfo() (batteryInfo BatteryInfo, err error) {
return 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:)] // [[FBRoute GET:@"/window/size"] respondWithTarget:self action:@selector(handleGetWindowSize:)]
if !wd.windowSize.IsNil() { if !wd.windowSize.IsNil() {
// use cached window size // use cached window size
@@ -217,16 +233,16 @@ func (wd *wdaDriver) WindowSize() (size Size, err error) {
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/window/size"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/window/size"); err != nil {
return 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 { 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 size = reply.Value.Size
scale, err := wd.Scale() scale, err := wd.Scale()
if err != nil { 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.Height = size.Height * int(scale)
size.Width = size.Width * int(scale) size.Width = size.Width * int(scale)
@@ -235,26 +251,21 @@ func (wd *wdaDriver) WindowSize() (size Size, err error) {
return wd.windowSize, nil 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:)] // [[FBRoute GET:@"/wda/screen"] respondWithTarget:self action:@selector(handleGetScreen:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/screen"); err != nil { 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 { if err = json.Unmarshal(rawResp, reply); err != nil {
return Screen{}, err return ai.Screen{}, err
} }
screen = reply.Value.Screen screen = reply.Value.Screen
return return
} }
func (wd *wdaDriver) GetTimestamp() (timestamp int64, err error) { func (wd *WDADriver) Scale() (float64, error) {
return 0, errors.Wrap(errDriverNotImplemented,
"GetTimestamp not implemented for ios")
}
func (wd *wdaDriver) Scale() (float64, error) {
if !builtin.IsZeroFloat64(wd.scale) { if !builtin.IsZeroFloat64(wd.scale) {
return wd.scale, nil return wd.scale, nil
} }
@@ -266,11 +277,11 @@ func (wd *wdaDriver) Scale() (float64, error) {
return screen.Scale, nil return screen.Scale, nil
} }
func (wd *wdaDriver) toScale(x float64) float64 { func (wd *WDADriver) toScale(x float64) float64 {
return x / wd.scale return x / wd.scale
} }
func (wd *wdaDriver) ActiveAppInfo() (info AppInfo, err error) { func (wd *WDADriver) ActiveAppInfo() (info AppInfo, err error) {
// [[FBRoute GET:@"/wda/activeAppInfo"] respondWithTarget:self action:@selector(handleActiveAppInfo:)] // [[FBRoute GET:@"/wda/activeAppInfo"] respondWithTarget:self action:@selector(handleActiveAppInfo:)]
// [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession // [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession
var rawResp rawResponse var rawResp rawResponse
@@ -285,7 +296,7 @@ func (wd *wdaDriver) ActiveAppInfo() (info AppInfo, err error) {
return 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:)] // [[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil {
@@ -299,7 +310,7 @@ func (wd *wdaDriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
return return
} }
func (wd *wdaDriver) AppState(bundleId string) (runState AppState, err error) { func (wd *WDADriver) AppState(bundleId string) (runState AppState, err error) {
// [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)] // [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)]
data := map[string]interface{}{"bundleId": bundleId} data := map[string]interface{}{"bundleId": bundleId}
var rawResp rawResponse var rawResp rawResponse
@@ -315,7 +326,7 @@ func (wd *wdaDriver) AppState(bundleId string) (runState AppState, err error) {
return 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"] respondWithTarget:self action:@selector(handleIsLocked:)]
// [[FBRoute GET:@"/wda/locked"].withoutSession // [[FBRoute GET:@"/wda/locked"].withoutSession
var rawResp rawResponse var rawResp rawResponse
@@ -328,27 +339,27 @@ func (wd *wdaDriver) IsLocked() (locked bool, err error) {
return 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"] respondWithTarget:self action:@selector(handleUnlock:)]
// [[FBRoute POST:@"/wda/unlock"].withoutSession // [[FBRoute POST:@"/wda/unlock"].withoutSession
_, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/unlock") _, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/unlock")
return 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"] respondWithTarget:self action:@selector(handleLock:)]
// [[FBRoute POST:@"/wda/lock"].withoutSession // [[FBRoute POST:@"/wda/lock"].withoutSession
_, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/lock") _, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/lock")
return return
} }
func (wd *wdaDriver) Homescreen() (err error) { func (wd *WDADriver) Homescreen() (err error) {
// [[FBRoute POST:@"/wda/homescreen"].withoutSession respondWithTarget:self action:@selector(handleHomescreenCommand:)] // [[FBRoute POST:@"/wda/homescreen"].withoutSession respondWithTarget:self action:@selector(handleHomescreenCommand:)]
_, err = wd.httpPOST(nil, "/wda/homescreen") _, err = wd.httpPOST(nil, "/wda/homescreen")
return 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"] respondWithTarget:self action:@selector(handleAlertGetTextCommand:)]
// [[FBRoute GET:@"/alert/text"].withoutSession // [[FBRoute GET:@"/alert/text"].withoutSession
var rawResp rawResponse var rawResp rawResponse
@@ -361,7 +372,7 @@ func (wd *wdaDriver) AlertText() (text string, err error) {
return 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:)] // [[FBRoute GET:@"/wda/alert/buttons"] respondWithTarget:self action:@selector(handleGetAlertButtonsCommand:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/alert/buttons"); err != nil { 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 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"] respondWithTarget:self action:@selector(handleAlertAcceptCommand:)]
// [[FBRoute POST:@"/alert/accept"].withoutSession // [[FBRoute POST:@"/alert/accept"].withoutSession
data := make(map[string]interface{}) data := make(map[string]interface{})
@@ -386,7 +397,7 @@ func (wd *wdaDriver) AlertAccept(label ...string) (err error) {
return 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"] respondWithTarget:self action:@selector(handleAlertDismissCommand:)]
// [[FBRoute POST:@"/alert/dismiss"].withoutSession // [[FBRoute POST:@"/alert/dismiss"].withoutSession
data := make(map[string]interface{}) data := make(map[string]interface{})
@@ -397,14 +408,14 @@ func (wd *wdaDriver) AlertDismiss(label ...string) (err error) {
return 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:)] // [[FBRoute POST:@"/alert/text"] respondWithTarget:self action:@selector(handleAlertSetTextCommand:)]
data := map[string]interface{}{"value": strings.Split(text, "")} data := map[string]interface{}{"value": strings.Split(text, "")}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/alert/text") _, err = wd.httpPOST(data, "/session", wd.sessionID, "/alert/text")
return 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:)] // [[FBRoute POST:@"/wda/apps/launch"] respondWithTarget:self action:@selector(handleSessionAppLaunch:)]
data := make(map[string]interface{}) data := make(map[string]interface{})
data["bundleId"] = bundleId data["bundleId"] = bundleId
@@ -419,7 +430,7 @@ func (wd *wdaDriver) AppLaunch(bundleId string) (err error) {
return nil 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:)] // [[FBRoute POST:@"/wda/apps/launchUnattached"].withoutSession respondWithTarget:self action:@selector(handleLaunchUnattachedApp:)]
data := map[string]interface{}{"bundleId": bundleId} data := map[string]interface{}{"bundleId": bundleId}
_, err = wd.httpPOST(data, "/wda/apps/launchUnattached") _, err = wd.httpPOST(data, "/wda/apps/launchUnattached")
@@ -430,7 +441,7 @@ func (wd *wdaDriver) AppLaunchUnattached(bundleId string) (err error) {
return nil 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:)] // [[FBRoute POST:@"/wda/apps/terminate"] respondWithTarget:self action:@selector(handleSessionAppTerminate:)]
data := map[string]interface{}{"bundleId": bundleId} data := map[string]interface{}{"bundleId": bundleId}
var rawResp rawResponse var rawResp rawResponse
@@ -443,14 +454,14 @@ func (wd *wdaDriver) AppTerminate(bundleId string) (successful bool, err error)
return 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:)] // [[FBRoute POST:@"/wda/apps/activate"] respondWithTarget:self action:@selector(handleSessionAppActivate:)]
data := map[string]interface{}{"bundleId": bundleId} data := map[string]interface{}{"bundleId": bundleId}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/activate") _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/activate")
return 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:)] // [[FBRoute POST:@"/wda/deactivateApp"] respondWithTarget:self action:@selector(handleDeactivateAppCommand:)]
if second < 3 { if second < 3 {
second = 3.0 second = 3.0
@@ -460,7 +471,7 @@ func (wd *wdaDriver) AppDeactivate(second float64) (err error) {
return return
} }
func (wd *wdaDriver) GetForegroundApp() (appInfo AppInfo, err error) { func (wd *WDADriver) GetForegroundApp() (appInfo AppInfo, err error) {
activeAppInfo, err := wd.ActiveAppInfo() activeAppInfo, err := wd.ActiveAppInfo()
appInfo.BundleId = activeAppInfo.BundleId appInfo.BundleId = activeAppInfo.BundleId
if err != nil { if err != nil {
@@ -484,7 +495,7 @@ func (wd *wdaDriver) GetForegroundApp() (appInfo AppInfo, err error) {
return appInfo, err 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). log.Debug().Str("bundleId", bundleId).
Strs("viewControllerType", viewControllerType). Strs("viewControllerType", viewControllerType).
Msg("assert ios foreground bundleId") Msg("assert ios foreground bundleId")
@@ -507,7 +518,7 @@ func (wd *wdaDriver) AssertForegroundApp(bundleId string, viewControllerType ...
return nil 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:)] // [[FBRoute POST:@"/wda/tap/:uuid"] respondWithTarget:self action:@selector(handleTap:)]
actionOptions := option.NewActionOptions(opts...) actionOptions := option.NewActionOptions(opts...)
@@ -531,7 +542,7 @@ func (wd *wdaDriver) Tap(x, y float64, opts ...option.ActionOption) (err error)
return 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:)] // [[FBRoute POST:@"/wda/doubleTap"] respondWithTarget:self action:@selector(handleDoubleTapCoordinate:)]
actionOptions := option.NewActionOptions(opts...) actionOptions := option.NewActionOptions(opts...)
x = wd.toScale(x) x = wd.toScale(x)
@@ -551,7 +562,7 @@ func (wd *wdaDriver) DoubleTap(x, y float64, opts ...option.ActionOption) (err e
return 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...) actionOptions := option.NewActionOptions(opts...)
if actionOptions.Duration == 0 { if actionOptions.Duration == 0 {
opts = append(opts, option.WithDuration(1)) 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...) 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:)] // [[FBRoute POST:@"/wda/dragfromtoforduration"] respondWithTarget:self action:@selector(handleDragCoordinate:)]
actionOptions := option.NewActionOptions(opts...) actionOptions := option.NewActionOptions(opts...)
@@ -596,11 +607,11 @@ func (wd *wdaDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionO
return 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...) 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:)] // [[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)]
data := map[string]interface{}{ data := map[string]interface{}{
"contentType": contentType, "contentType": contentType,
@@ -610,7 +621,7 @@ func (wd *wdaDriver) SetPasteboard(contentType PasteboardType, content string) (
return return
} }
func (wd *wdaDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) { func (wd *WDADriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffer, err error) {
// [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)] // [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)]
data := map[string]interface{}{"contentType": contentType} data := map[string]interface{}{"contentType": contentType}
var rawResp rawResponse var rawResp rawResponse
@@ -623,15 +634,15 @@ func (wd *wdaDriver) GetPasteboard(contentType PasteboardType) (raw *bytes.Buffe
return return
} }
func (wd *wdaDriver) SetIme(ime string) error { func (wd *WDADriver) SetIme(ime string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (wd *wdaDriver) PressKeyCode(keyCode KeyCode) (err error) { func (wd *WDADriver) PressKeyCode(keyCode KeyCode) (err error) {
return errDriverNotImplemented 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:)] // [[FBRoute POST:@"/wda/keys"] respondWithTarget:self action:@selector(handleKeys:)]
actionOptions := option.NewActionOptions(opts...) actionOptions := option.NewActionOptions(opts...)
data := map[string]interface{}{"value": strings.Split(text, "")} data := map[string]interface{}{"value": strings.Split(text, "")}
@@ -643,7 +654,7 @@ func (wd *wdaDriver) SendKeys(text string, opts ...option.ActionOption) (err err
return 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 { if count == 0 {
return nil return nil
} }
@@ -657,16 +668,16 @@ func (wd *wdaDriver) Backspace(count int, opts ...option.ActionOption) (err erro
return 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...) return wd.SendKeys(text, opts...)
} }
func (wd *wdaDriver) Clear(packageName string) error { func (wd *WDADriver) Clear(packageName string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
// PressBack simulates a short press on the BACK button. // 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...) actionOptions := option.NewActionOptions(opts...)
windowSize, err := wd.WindowSize() windowSize, err := wd.WindowSize()
@@ -702,27 +713,27 @@ func (wd *wdaDriver) PressBack(opts ...option.ActionOption) (err error) {
return 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:)] // [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)]
data := map[string]interface{}{"name": devBtn} data := map[string]interface{}{"name": devBtn}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton") _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton")
return 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 return info, errDriverNotImplemented
} }
func (wd *wdaDriver) LogoutNoneUI(packageName string) error { func (wd *WDADriver) LogoutNoneUI(packageName string) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (wd *wdaDriver) StartCamera() (err error) { func (wd *WDADriver) StartCamera() (err error) {
// start camera, alias for app_launch com.apple.camera // start camera, alias for app_launch com.apple.camera
return wd.AppLaunch("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 // stop camera, alias for app_terminate com.apple.camera
success, err := wd.AppTerminate("com.apple.camera") success, err := wd.AppTerminate("com.apple.camera")
if err != nil { if err != nil {
@@ -734,7 +745,7 @@ func (wd *wdaDriver) StopCamera() (err error) {
return nil 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:)] // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil {
@@ -748,14 +759,14 @@ func (wd *wdaDriver) Orientation() (orientation Orientation, err error) {
return 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:)] // [[FBRoute POST:@"/orientation"] respondWithTarget:self action:@selector(handleSetOrientation:)]
data := map[string]interface{}{"orientation": orientation} data := map[string]interface{}{"orientation": orientation}
_, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation") _, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation")
return return
} }
func (wd *wdaDriver) Rotation() (rotation Rotation, err error) { func (wd *WDADriver) Rotation() (rotation Rotation, err error) {
// [[FBRoute GET:@"/rotation"] respondWithTarget:self action:@selector(handleGetRotation:)] // [[FBRoute GET:@"/rotation"] respondWithTarget:self action:@selector(handleGetRotation:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/rotation"); err != nil { if rawResp, err = wd.httpGET("/session", wd.sessionID, "/rotation"); err != nil {
@@ -769,13 +780,13 @@ func (wd *wdaDriver) Rotation() (rotation Rotation, err error) {
return 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:)] // [[FBRoute POST:@"/rotation"] respondWithTarget:self action:@selector(handleSetRotation:)]
_, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation") _, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation")
return return
} }
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"] respondWithTarget:self action:@selector(handleGetScreenshot:)]
// [[FBRoute GET:@"/screenshot"].withoutSession respondWithTarget:self action:@selector(handleGetScreenshot:)] // [[FBRoute GET:@"/screenshot"].withoutSession respondWithTarget:self action:@selector(handleGetScreenshot:)]
var rawResp rawResponse var rawResp rawResponse
@@ -791,7 +802,7 @@ func (wd *wdaDriver) Screenshot() (raw *bytes.Buffer, err error) {
return 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"] respondWithTarget:self action:@selector(handleGetSourceCommand:)]
// [[FBRoute GET:@"/source"].withoutSession // [[FBRoute GET:@"/source"].withoutSession
tmp, _ := url.Parse(wd.concatURL(nil, "/session", wd.sessionID)) 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 return
} }
func (wd *wdaDriver) TapByText(text string, opts ...option.ActionOption) error { func (wd *WDADriver) TapByText(text string, opts ...option.ActionOption) error {
return errDriverNotImplemented return errDriverNotImplemented
} }
func (wd *wdaDriver) TapByTexts(actions ...TapTextAction) error { func (wd *WDADriver) TapByTexts(actions ...TapTextAction) error {
return errDriverNotImplemented 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"] respondWithTarget:self action:@selector(handleGetAccessibleSourceCommand:)]
// [[FBRoute GET:@"/wda/accessibleSource"].withoutSession // [[FBRoute GET:@"/wda/accessibleSource"].withoutSession
var rawResp rawResponse var rawResp rawResponse
@@ -848,13 +859,13 @@ func (wd *wdaDriver) AccessibleSource() (source string, err error) {
return return
} }
func (wd *wdaDriver) HealthCheck() (err error) { func (wd *WDADriver) HealthCheck() (err error) {
// [[FBRoute GET:@"/wda/healthcheck"].withoutSession respondWithTarget:self action:@selector(handleGetHealthCheck:)] // [[FBRoute GET:@"/wda/healthcheck"].withoutSession respondWithTarget:self action:@selector(handleGetHealthCheck:)]
_, err = wd.httpGET("/wda/healthcheck") _, err = wd.httpGET("/wda/healthcheck")
return 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:)] // [[FBRoute GET:@"/appium/settings"] respondWithTarget:self action:@selector(handleGetSettings:)]
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/session", wd.sessionID, "/appium/settings"); err != nil { 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 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:)] // [[FBRoute POST:@"/appium/settings"] respondWithTarget:self action:@selector(handleSetSettings:)]
data := map[string]interface{}{"settings": settings} data := map[string]interface{}{"settings": settings}
var rawResp rawResponse var rawResp rawResponse
@@ -883,7 +894,7 @@ func (wd *wdaDriver) SetAppiumSettings(settings map[string]interface{}) (ret map
return return
} }
func (wd *wdaDriver) IsHealthy() (healthy bool, err error) { func (wd *WDADriver) IsHealthy() (healthy bool, err error) {
var rawResp rawResponse var rawResp rawResponse
if rawResp, err = wd.httpGET("/health"); err != nil { if rawResp, err = wd.httpGET("/health"); err != nil {
return false, err return false, err
@@ -894,17 +905,17 @@ func (wd *wdaDriver) IsHealthy() (healthy bool, err error) {
return true, nil return true, nil
} }
func (wd *wdaDriver) WdaShutdown() (err error) { func (wd *WDADriver) WdaShutdown() (err error) {
_, err = wd.httpGET("/wda/shutdown") _, err = wd.httpGET("/wda/shutdown")
return 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:)] // [[FBRoute POST:@"/gtf/automation/log"].withoutSession respondWithTarget:self action:@selector(handleAutomationLog:)]
return wd.httpPOST(data, "/gtf/automation/log") 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) 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) 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") log.Info().Msg("start WDA log recording")
if identifier == nil { if identifier == nil {
identifier = []string{""} identifier = []string{""}
@@ -988,7 +999,7 @@ type wdaResponse struct {
SessionID string `json:"sessionId"` 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") log.Info().Msg("stop log recording")
data := map[string]interface{}{"action": "stop"} data := map[string]interface{}{"action": "stop"}
rawResp, err := wd.triggerWDALog(data) rawResp, err := wd.triggerWDALog(data)
@@ -1007,18 +1018,22 @@ func (wd *wdaDriver) StopCaptureLog() (result interface{}, err error) {
return reply.Value, nil return reply.Value, nil
} }
func (wd *wdaDriver) GetSession() *Session { func (wd *WDADriver) GetSession() *Session {
return wd.Session return wd.Session
} }
func (wd *wdaDriver) GetDriverResults() []*DriverRequests { func (wd *WDADriver) GetDriverResults() []*DriverRequests {
defer func() { defer func() {
wd.requests = nil wd.requests = nil
}() }()
return wd.requests return wd.requests
} }
func (wd *wdaDriver) TearDown() error { func (wd *WDADriver) Setup() error {
return nil
}
func (wd *WDADriver) TearDown() error {
wd.mjpegClient.CloseIdleConnections() wd.mjpegClient.CloseIdleConnections()
wd.client.CloseIdleConnections() wd.client.CloseIdleConnections()
return nil return nil
+1 -1
View File
@@ -471,7 +471,7 @@ func Test_remoteWD_AccessibleSource(t *testing.T) {
func TestRecord(t *testing.T) { func TestRecord(t *testing.T) {
setup(t) setup(t)
path, err := driver.(*wdaDriver).RecordScreen("", 5*time.Second) path, err := driver.(*WDADriver).RecordScreen("", 5*time.Second)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
+3
View File
@@ -260,6 +260,9 @@ func NewActionOptions(opts ...ActionOption) *ActionOptions {
for _, option := range opts { for _, option := range opts {
option(actionOptions) option(actionOptions)
} }
if actionOptions.MaxRetryTimes == 0 {
actionOptions.MaxRetryTimes = 1
}
return actionOptions return actionOptions
} }
+3 -1
View File
@@ -1,6 +1,8 @@
package option package option
import "github.com/httprunner/funplugin" import (
"github.com/httprunner/funplugin"
)
type DriverOptions struct { type DriverOptions struct {
Capabilities Capabilities Capabilities Capabilities
-30
View File
@@ -2,7 +2,6 @@ package uixt
import ( import (
"fmt" "fmt"
"math"
) )
type DeviceStatus struct { 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 { type AppInfo struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
AppBaseInfo AppBaseInfo
@@ -274,18 +259,3 @@ const (
DirectionLeft Direction = "left" DirectionLeft Direction = "left"
DirectionRight Direction = "right" 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
}