From a1a95cff8c90bc37145d45ed3c36faa295a49a6a Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 23 Aug 2022 20:45:34 +0800 Subject: [PATCH] feat: init device with optional serial, port, mjpeg port --- docs/CHANGELOG.md | 4 +++ go.mod | 1 + go.sum | 4 +-- hrp/config.go | 25 +++++++++++++++++- hrp/runner.go | 10 +++++++- hrp/step.go | 4 +-- hrp/step_android_ui.go | 2 +- hrp/step_ios_ui.go | 57 +++++++++++++++++++---------------------- hrp/step_ios_ui_test.go | 3 ++- hrp/step_request.go | 4 +-- 10 files changed, 74 insertions(+), 40 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index afb0344d..411982f1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History +## v4.2.1 (2022-08-23) + +- feat: support iOS UI automation with WebDriverAgent + ## v4.2.0 (2022-08-21) **go version** diff --git a/go.mod b/go.mod index 6eb56b61..1853a9af 100644 --- a/go.mod +++ b/go.mod @@ -38,3 +38,4 @@ require ( ) // replace github.com/httprunner/funplugin => ../funplugin +replace github.com/electricbubble/gwda => github.com/debugtalk/gwda v0.0.0-20220823102718-fae698f66992 diff --git a/go.sum b/go.sum index 09756429..294e362f 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/debugtalk/gwda v0.0.0-20220823102718-fae698f66992 h1:z5dL+1EechBejcrY+zpeGUTuvUDnP6bTWroyhdqwzas= +github.com/debugtalk/gwda v0.0.0-20220823102718-fae698f66992/go.mod h1:0kmE3KaUs6RECo+wkeSbmfJjQb/anVphrf3mvFvssJ0= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -101,8 +103,6 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/electricbubble/gidevice v0.6.2 h1:eIeCHH7Xn5fTwnUv3qL8c7L4anKIHtjlTBkgr1LDVTc= github.com/electricbubble/gidevice v0.6.2/go.mod h1:bRHL2M9qgeEKju8KRvKMZUVEg7t5zMnTiG3SJ3QDH5o= -github.com/electricbubble/gwda v0.4.0 h1:+Sbi8WRM8sXh0cXpmY97GiWuR1CNQ/v2tsMUyWxlnV4= -github.com/electricbubble/gwda v0.4.0/go.mod h1:kyzKpP1/iKJ2i4AxmT8sEmSvB8Pz5NcDVwc/m/Jsg6k= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= diff --git a/hrp/config.go b/hrp/config.go index 3ee1264a..103ea986 100644 --- a/hrp/config.go +++ b/hrp/config.go @@ -29,6 +29,7 @@ type TConfig struct { ParametersSetting *TParamsConfig `json:"parameters_setting,omitempty" yaml:"parameters_setting,omitempty"` ThinkTimeSetting *ThinkTimeConfig `json:"think_time,omitempty" yaml:"think_time,omitempty"` WebSocketSetting *WebSocketConfig `json:"websocket,omitempty" yaml:"websocket,omitempty"` + IOS []*IOSConfig `json:"ios,omitempty" yaml:"ios,omitempty"` Timeout float64 `json:"timeout,omitempty" yaml:"timeout,omitempty"` // global timeout in seconds Export []string `json:"export,omitempty" yaml:"export,omitempty"` Weight int `json:"weight,omitempty" yaml:"weight,omitempty"` @@ -90,12 +91,34 @@ func (c *TConfig) SetWeight(weight int) *TConfig { return c } -func (c *TConfig) SetWebSocket(times, interval, timeout, size int64) { +func (c *TConfig) SetWebSocket(times, interval, timeout, size int64) *TConfig { c.WebSocketSetting = &WebSocketConfig{ ReconnectionTimes: times, ReconnectionInterval: interval, MaxMessageSize: size, } + return c +} + +func (c *TConfig) SetIOS(device WDADevice) *TConfig { + // each device can have its own settings + if device.UDID != "" { + c.IOS = append(c.IOS, &IOSConfig{ + WDADevice: device, + }) + return c + } + + // device UDID is not specified ,settings will be shared + iosConfig := &IOSConfig{ + WDADevice: device, + } + if len(c.IOS) == 0 { + c.IOS = append(c.IOS, iosConfig) + } else { + c.IOS[0] = iosConfig + } + return c } type ThinkTimeConfig struct { diff --git a/hrp/runner.go b/hrp/runner.go index d4e4f9b1..0a7002f9 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -12,12 +12,12 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/httprunner/funplugin" "github.com/jinzhu/copier" "github.com/pkg/errors" "github.com/rs/zerolog/log" "golang.org/x/net/http2" - "github.com/httprunner/funplugin" "github.com/httprunner/httprunner/v4/hrp/internal/builtin" "github.com/httprunner/httprunner/v4/hrp/internal/sdk" ) @@ -384,6 +384,14 @@ func (r *testCaseRunner) parseConfig() error { } r.parametersIterator = parametersIterator + // init iOS WDA clients + for _, iosDeviceConfig := range r.parsedConfig.IOS { + _, err := r.hrpRunner.InitWDAClient(iosDeviceConfig.WDADevice) + if err != nil { + return errors.Wrap(err, "init iOS WDA client failed") + } + } + return nil } diff --git a/hrp/step.go b/hrp/step.go index 4af50755..63390399 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -75,8 +75,8 @@ type TStep struct { Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"` WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"` - Android *AndroidAction `json:"android,omitempty" yaml:"android,omitempty"` - IOS *IOSAction `json:"ios,omitempty" yaml:"ios,omitempty"` + Android *AndroidStep `json:"android,omitempty" yaml:"android,omitempty"` + IOS *IOSStep `json:"ios,omitempty" yaml:"ios,omitempty"` Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` diff --git a/hrp/step_android_ui.go b/hrp/step_android_ui.go index d3fecb26..14eb76d8 100644 --- a/hrp/step_android_ui.go +++ b/hrp/step_android_ui.go @@ -2,7 +2,7 @@ package hrp import "fmt" -type AndroidAction struct { +type AndroidStep struct { MobileAction Serial string `json:"serial,omitempty" yaml:"serial,omitempty"` Actions []MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"` diff --git a/hrp/step_ios_ui.go b/hrp/step_ios_ui.go index a4987341..016745d0 100644 --- a/hrp/step_ios_ui.go +++ b/hrp/step_ios_ui.go @@ -32,9 +32,19 @@ const ( dismissAlertButtonSelector = "**/XCUIElementTypeButton[`label IN {'不允许','暂不'}`]" ) -type IOSAction struct { +type IOSConfig struct { + WDADevice +} + +type WDADevice struct { + UDID string `json:"udid,omitempty" yaml:"udid,omitempty"` + Port int `json:"port,omitempty" yaml:"port,omitempty"` + MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` +} + +type IOSStep struct { + WDADevice MobileAction - UDID string `json:"udid,omitempty" yaml:"udid,omitempty"` Actions []MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"` } @@ -318,7 +328,7 @@ func (s *StepIOSValidation) Run(r *SessionRunner) (*StepResult, error) { return runStepIOS(r, s.step) } -func (r *HRPRunner) InitWDAClient(udid string) (client *wdaClient, err error) { +func (r *HRPRunner) InitWDAClient(device WDADevice) (client *wdaClient, err error) { defer func() { if err != nil { return @@ -336,13 +346,24 @@ func (r *HRPRunner) InitWDAClient(udid string) (client *wdaClient, err error) { }() // avoid duplicate init - if udid == "" && len(r.wdaClients) == 1 { + if device.UDID == "" && len(r.wdaClients) == 1 { for _, v := range r.wdaClients { return v, nil } } - targetDevice, err := getAttachedIOSDevice(udid) + // init wda device + var options []gwda.DeviceOptions + if device.UDID != "" { + options = append(options, gwda.WithSerialNumber(device.UDID)) + } + if device.Port != 0 { + options = append(options, gwda.WithPort(device.Port)) + } + if device.MjpegPort != 0 { + options = append(options, gwda.WithMjpegPort(device.MjpegPort)) + } + targetDevice, err := gwda.NewDevice(options...) if err != nil { return nil, err } @@ -395,30 +416,6 @@ func (r *HRPRunner) InitWDAClient(udid string) (client *wdaClient, err error) { return client, nil } -func getAttachedIOSDevice(udid string) (*gwda.Device, error) { - // get all attached deivces - devices, err := gwda.DeviceList() - if err != nil { - return nil, errors.Wrap(err, "failed to get attached ios devices list") - } - if len(devices) == 0 { - return nil, errors.New("no ios devices attached") - } - - if udid == "" { - return &devices[0], nil - } - - // find device by udid - for _, device := range devices { - if device.SerialNumber() == udid { - return &device, nil - } - } - - return nil, fmt.Errorf("device %s is not attached", udid) -} - func runStepIOS(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) { stepResult = &StepResult{ Name: step.Name, @@ -428,7 +425,7 @@ func runStepIOS(r *SessionRunner, step *TStep) (stepResult *StepResult, err erro } // init wdaClient driver - wdaClient, err := r.hrpRunner.InitWDAClient(step.IOS.UDID) + wdaClient, err := r.hrpRunner.InitWDAClient(step.IOS.WDADevice) if err != nil { return } diff --git a/hrp/step_ios_ui_test.go b/hrp/step_ios_ui_test.go index 0ed9120f..78972278 100644 --- a/hrp/step_ios_ui_test.go +++ b/hrp/step_ios_ui_test.go @@ -70,7 +70,8 @@ func TestIOSAppLaunch(t *testing.T) { func TestIOSWeixinLive(t *testing.T) { testCase := &TestCase{ - Config: NewConfig("ios ui action on 微信直播"), + Config: NewConfig("ios ui action on 微信直播"). + SetIOS(WDADevice{Port: 8700, MjpegPort: 8800}), TestSteps: []IStep{ NewStep("启动微信"). IOS(). diff --git a/hrp/step_request.go b/hrp/step_request.go index 9c44497b..48a70e4d 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -764,7 +764,7 @@ func (s *StepRequest) WebSocket() *StepWebSocket { // Android creates a new android action func (s *StepRequest) Android() *StepAndroid { - s.step.Android = &AndroidAction{} + s.step.Android = &AndroidStep{} return &StepAndroid{ step: s.step, } @@ -772,7 +772,7 @@ func (s *StepRequest) Android() *StepAndroid { // IOS creates a new ios action func (s *StepRequest) IOS() *StepIOS { - s.step.IOS = &IOSAction{} + s.step.IOS = &IOSStep{} return &StepIOS{ step: s.step, }