mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-07 00:17:37 +08:00
refactor: split ai related logic to pkg/ai
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v5.0.0+2502071814
|
v5.0.0+2502081446
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package ai
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestOption(t *testing.T) {
|
||||||
|
options := NewAIService(
|
||||||
|
WithCVService(CVServiceTypeOpenCV),
|
||||||
|
WithLLMService(LLMServiceTypeDeepSeekV3),
|
||||||
|
)
|
||||||
|
t.Log(options)
|
||||||
|
}
|
||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
@@ -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))
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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()
|
||||||
|
|||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user