feat: implement ios ui swipe

This commit is contained in:
debugtalk
2022-07-27 10:22:04 +08:00
parent acae99cd8b
commit 3fd3dab9ae
5 changed files with 183 additions and 3 deletions

1
go.mod
View File

@@ -5,6 +5,7 @@ go 1.16
require (
github.com/andybalholm/brotli v1.0.4
github.com/denisbrodbeck/machineid v1.0.1
github.com/electricbubble/gwda v0.3.0
github.com/fatih/color v1.13.0
github.com/getsentry/sentry-go v0.13.0
github.com/go-errors/errors v1.0.1

9
go.sum
View File

@@ -99,6 +99,10 @@ github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6ps
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/electricbubble/gidevice v0.0.4 h1:PbOt4AngNQTtO5j0vCZ3Xcj9mByDtZmjBYLTh8PJ9kc=
github.com/electricbubble/gidevice v0.0.4/go.mod h1:hWRHIPf4uyiEB56hnVHVvu6MoVg7RlJY8ZV2FVgLKZA=
github.com/electricbubble/gwda v0.3.0 h1:uQMZxmp5D51iMsXrWfi21MlftrkPmOeLDE+gtw06fg4=
github.com/electricbubble/gwda v0.3.0/go.mod h1:co3ynSIVXEyI3aKdzfjqkFDFloFcxhc+e27U0ajyZsM=
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=
@@ -270,6 +274,7 @@ github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jinzhu/copier v0.3.2 h1:QdBOCbaouLDYaIPFfi1bKv5F5tPpeTwXe4sD0jqtz5w=
@@ -419,6 +424,8 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -900,6 +907,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v0.0.0-20201203080718-1454fab16a06 h1:QDxUo/w2COstK1wIBYpzQlHX/NqaQTcf9jyz347nI58=
howett.net/plist v0.0.0-20201203080718-1454fab16a06/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -48,6 +48,7 @@ func NewRunner(t *testing.T) *HRPRunner {
Transport: &http2.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Jar: jar, // insert response cookies into request
Timeout: 120 * time.Second,
},
// use default handshake timeout (no timeout limit) here, enable timeout at step level
@@ -69,6 +70,7 @@ type HRPRunner struct {
httpClient *http.Client
http2Client *http.Client
wsDialer *websocket.Dialer
wdaClients map[string]*wdaClient // wda client used for iOS UI automation, key is udid
}
// SetClientTransport configures transport of http client for high concurrency load testing

View File

@@ -1,5 +1,13 @@
package hrp
import (
"fmt"
"github.com/electricbubble/gwda"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
type IOSAction struct {
MobileAction
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
@@ -159,12 +167,172 @@ func (s *StepIOSValidation) Run(r *SessionRunner) (*StepResult, error) {
return runStepIOS(r, s.step)
}
func (r *HRPRunner) InitWDAClient(udid string) (*wdaClient, error) {
// avoid duplicate init
if udid == "" && len(r.wdaClients) == 1 {
for _, v := range r.wdaClients {
return v, nil
}
}
targetDevice, err := getAttachedIOSDevice(udid)
if err != nil {
return nil, err
}
// avoid duplicate init
if client, ok := r.wdaClients[targetDevice.SerialNumber()]; ok {
return client, nil
}
// init WDA driver
driver, err := gwda.NewUSBDriver(nil, *targetDevice)
if err != nil {
return nil, errors.Wrap(err, "failed to init WDA driver")
}
// get device window size
windowSize, err := driver.WindowSize()
if err != nil {
return nil, errors.Wrap(err, "failed to get windows size")
}
// cache wda client
r.wdaClients = make(map[string]*wdaClient)
client := &wdaClient{
Driver: driver,
WindowSize: windowSize,
}
r.wdaClients[targetDevice.SerialNumber()] = client
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,
StepType: stepTypeAndroid,
StepType: stepTypeIOS,
Success: false,
ContentSize: 0,
}
// init wdaClient driver
wdaClient, err := r.hrpRunner.InitWDAClient(step.IOS.UDID)
if err != nil {
return
}
// prepare actions
var actions []MobileAction
if step.IOS.Actions == nil {
actions = []MobileAction{
{
Method: step.IOS.Method,
Params: step.IOS.Params,
},
}
} else {
actions = step.IOS.Actions
}
// run actions
for _, action := range actions {
if err := wdaClient.doAction(action); err != nil {
return stepResult, err
}
}
// do validation
// step.Validators
stepResult.Success = true
return stepResult, nil
}
var errActionNotImplemented = errors.New("UI action not implemented")
type wdaClient struct {
Driver gwda.WebDriver
WindowSize gwda.Size
}
func (w *wdaClient) doAction(action MobileAction) error {
log.Info().Str("method", string(action.Method)).Interface("params", action.Params).Msg("start iOS UI action")
switch action.Method {
case appInstall:
// TODO
return errActionNotImplemented
case appStart:
// TODO
return errActionNotImplemented
case uiClick:
// TODO
return errActionNotImplemented
case uiDoubleClick:
// TODO
return errActionNotImplemented
case uiLongClick:
// TODO
return errActionNotImplemented
case uiSwipe:
width := w.WindowSize.Width
height := w.WindowSize.Height
var fromX, fromY, toX, toY int
if direction, ok := action.Params.(string); ok {
switch direction {
case "up":
fromX, fromY, toX, toY = width/2, height*1/4, width/2, height*3/4
case "down":
fromX, fromY, toX, toY = width/2, height*3/4, width/2, height*1/4
case "left":
fromX, fromY, toX, toY = width*3/4, height/2, width*1/4, height/2
case "right":
fromX, fromY, toX, toY = width*1/4, height/2, width*3/4, height/2
}
} else if params, ok := action.Params.([]int); ok {
if len(params) != 4 {
return fmt.Errorf("invalid swipe params: %v", params)
}
fromX, fromY, toX, toY = params[0], params[1], params[2], params[3]
}
return w.Driver.Swipe(fromX, fromY, toX, toY)
case uiInput:
// TODO
return errActionNotImplemented
case appClick:
// TODO
return errActionNotImplemented
}
return nil
}
func (w *wdaClient) doValidation() error {
// TODO
return errActionNotImplemented
}

View File

@@ -32,12 +32,12 @@ func TestIOSAction(t *testing.T) {
Config: NewConfig("ios ui action"),
TestSteps: []IStep{
NewStep("launch douyin").
IOS().UDID("xxx").Click("抖音").
IOS().Click("抖音").
Validate().
AssertTextExists("首页", "首页 tab 不存在").
AssertTextExists("消息", "消息 tab 不存在"),
NewStep("swipe up and down").
IOS().UDID("xxx").SwipeUp().SwipeUp().SwipeDown(),
IOS().SwipeUp().SwipeUp().SwipeDown(),
},
}
tCase := testCase.ToTCase()