refactor: merge ios and android style

This commit is contained in:
debugtalk
2022-10-12 22:38:52 +08:00
parent 6b2b662bb1
commit c22b38f76f
11 changed files with 212 additions and 781 deletions
+2 -2
View File
@@ -6,8 +6,8 @@
}, },
"ios": [ "ios": [
{ {
"port": 8100, "port": 8700,
"mjpeg_port": 9100, "mjpeg_port": 8800,
"log_on": true "log_on": true
} }
] ]
+2 -2
View File
@@ -3,8 +3,8 @@ config:
variables: variables:
app_name: 抖音 app_name: 抖音
ios: ios:
- port: 8100 - port: 8700
mjpeg_port: 9100 mjpeg_port: 8800
log_on: true log_on: true
teststeps: teststeps:
- name: 启动抖音 - name: 启动抖音
@@ -16,8 +16,8 @@ func TestIOSDouyinFollowLive(t *testing.T) {
}). }).
SetIOS( SetIOS(
hrp.WithLogOn(true), hrp.WithLogOn(true),
hrp.WithWDAPort(8100), hrp.WithWDAPort(8700),
hrp.WithWDAMjpegPort(9100), hrp.WithWDAMjpegPort(8800),
), ),
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音"). hrp.NewStep("启动抖音").
-1
View File
@@ -406,7 +406,6 @@ func (r *testCaseRunner) parseConfig() error {
} }
iosDeviceConfig.UDID = udid.(string) iosDeviceConfig.UDID = udid.(string)
} }
device, err := uixt.NewIOSDevice(uixt.GetIOSDeviceOptions(iosDeviceConfig)...) device, err := uixt.NewIOSDevice(uixt.GetIOSDeviceOptions(iosDeviceConfig)...)
if err != nil { if err != nil {
return errors.Wrap(err, "init iOS device failed") return errors.Wrap(err, "init iOS device failed")
+2 -2
View File
@@ -71,8 +71,8 @@ type TStep struct {
Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"`
ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"` ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"`
WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"` WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"`
Android *AndroidStep `json:"android,omitempty" yaml:"android,omitempty"` Android *MobileStep `json:"android,omitempty" yaml:"android,omitempty"`
IOS *IOSStep `json:"ios,omitempty" yaml:"ios,omitempty"` IOS *MobileStep `json:"ios,omitempty" yaml:"ios,omitempty"`
Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"` Variables map[string]interface{} `json:"variables,omitempty" yaml:"variables,omitempty"`
SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"` SetupHooks []string `json:"setup_hooks,omitempty" yaml:"setup_hooks,omitempty"`
TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"` TeardownHooks []string `json:"teardown_hooks,omitempty" yaml:"teardown_hooks,omitempty"`
-603
View File
@@ -1,603 +0,0 @@
package hrp
import (
"fmt"
"time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
)
var (
WithSerialNumber = uixt.WithSerialNumber
WithAdbIP = uixt.WithAdbIP
WithAdbPort = uixt.WithAdbPort
WithAdbLogOn = uixt.WithAdbLogOn
)
type AndroidStep struct {
uixt.AndroidDevice `yaml:",inline"` // inline refers to https://pkg.go.dev/gopkg.in/yaml.v3#Marshal
uixt.MobileAction
Actions []uixt.MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"`
}
// StepAndroid implements IStep interface.
type StepAndroid struct {
step *TStep
}
func (s *StepAndroid) Serial(serial string) *StepAndroid {
s.step.Android.SerialNumber = serial
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) InstallApp(path string) *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.AppInstall,
Params: path,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) AppLaunch(bundleId string) *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.AppLaunch,
Params: bundleId,
})
return s
}
func (s *StepAndroid) AppLaunchUnattached(bundleId string) *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.AppLaunchUnattached,
Params: bundleId,
})
return s
}
func (s *StepAndroid) AppTerminate(bundleId string) *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.AppTerminate,
Params: bundleId,
})
return s
}
func (s *StepAndroid) Home() *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.ACTION_Home,
Params: nil,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) StartAppByIntent(activity string) *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.AppStart,
Params: activity,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) StartCamera() *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.CtlStartCamera,
Params: nil,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) StopCamera() *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.CtlStopCamera,
Params: nil,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) StartRecording() *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.RecordStart,
Params: nil,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) StopRecording() *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.RecordStop,
Params: nil,
})
return &StepAndroid{step: s.step}
}
// TapXY taps the point {X,Y}, X & Y is percentage of coordinates
func (s *StepAndroid) TapXY(x, y float64, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_TapXY,
Params: []float64{x, y},
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
// TapAbsXY taps the point {X,Y}, X & Y is absolute coordinates
func (s *StepAndroid) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_TapAbsXY,
Params: []float64{x, y},
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
// Tap taps on the target element
func (s *StepAndroid) Tap(params string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Tap,
Params: params,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
// Tap taps on the target element by OCR recognition
func (s *StepAndroid) TapByOCR(ocrText string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_TapByOCR,
Params: ocrText,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
// Tap taps on the target element by CV recognition
func (s *StepAndroid) TapByCV(imagePath string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_TapByCV,
Params: imagePath,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) DoubleTap(params string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_DoubleTap,
Params: params,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: []float64{sx, sy, ex, ey},
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeUp(options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "up",
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeDown(options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "down",
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeLeft(options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "left",
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeRight(options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Swipe,
Params: "right",
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) Input(text string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_Input,
Params: text,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
// Times specify running times for run last action
func (s *StepAndroid) Times(n int) *StepAndroid {
if n <= 0 {
log.Warn().Int("n", n).Msg("times should be positive, set to 1")
n = 1
}
actionsTotal := len(s.step.Android.Actions)
if actionsTotal == 0 {
return s
}
// actionsTotal >=1 && n >= 1
lastAction := s.step.Android.Actions[actionsTotal-1 : actionsTotal][0]
for i := 0; i < n-1; i++ {
// duplicate last action n-1 times
s.step.Android.Actions = append(s.step.Android.Actions, lastAction)
}
return &StepAndroid{step: s.step}
}
// Sleep specify sleep seconds after last action
func (s *StepAndroid) Sleep(n float64) *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.CtlSleep,
Params: n,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) ScreenShot() *StepAndroid {
s.step.Android.Actions = append(s.step.Android.Actions, uixt.MobileAction{
Method: uixt.CtlScreenShot,
Params: nil,
})
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeToTapApp(appName string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapApp,
Params: appName,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeToTapText(text string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapText,
Params: text,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
func (s *StepAndroid) SwipeToTapTexts(texts []string, options ...uixt.ActionOption) *StepAndroid {
action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapTexts,
Params: texts,
}
for _, option := range options {
option(&action)
}
s.step.Android.Actions = append(s.step.Android.Actions, action)
return &StepAndroid{step: s.step}
}
// Validate switches to step validation.
func (s *StepAndroid) Validate() *StepAndroidValidation {
return &StepAndroidValidation{
step: s.step,
}
}
func (s *StepAndroid) Name() string {
return s.step.Name
}
func (s *StepAndroid) Type() StepType {
return stepTypeAndroid
}
func (s *StepAndroid) Struct() *TStep {
return s.step
}
func (s *StepAndroid) Run(r *SessionRunner) (*StepResult, error) {
return runStepAndroid(r, s.step)
}
// StepAndroidValidation implements IStep interface.
type StepAndroidValidation struct {
step *TStep
}
func (s *StepAndroidValidation) AssertNameExists(expectedName string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorName,
Assert: uixt.AssertionExists,
Expect: expectedName,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("[%s] not found", expectedName)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertNameNotExists(expectedName string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorName,
Assert: uixt.AssertionNotExists,
Expect: expectedName,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("[%s] should not exist", expectedName)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertLabelExists(expectedLabel string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorLabel,
Assert: uixt.AssertionExists,
Expect: expectedLabel,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("attribute label [%s] not found", expectedLabel)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertLabelNotExists(expectedLabel string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorLabel,
Assert: uixt.AssertionNotExists,
Expect: expectedLabel,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("attribute label [%s] should not exist", expectedLabel)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertOCRExists(expectedText string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorOCR,
Assert: uixt.AssertionExists,
Expect: expectedText,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("ocr text [%s] not found", expectedText)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertOCRNotExists(expectedText string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorOCR,
Assert: uixt.AssertionNotExists,
Expect: expectedText,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("ocr text [%s] should not exist", expectedText)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertImageExists(expectedImagePath string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorImage,
Assert: uixt.AssertionExists,
Expect: expectedImagePath,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("cv image [%s] not found", expectedImagePath)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) AssertImageNotExists(expectedImagePath string, msg ...string) *StepAndroidValidation {
v := Validator{
Check: uixt.SelectorImage,
Assert: uixt.AssertionNotExists,
Expect: expectedImagePath,
}
if len(msg) > 0 {
v.Message = msg[0]
} else {
v.Message = fmt.Sprintf("cv image [%s] should not exist", expectedImagePath)
}
s.step.Validators = append(s.step.Validators, v)
return s
}
func (s *StepAndroidValidation) Name() string {
return s.step.Name
}
func (s *StepAndroidValidation) Type() StepType {
return stepTypeAndroid
}
func (s *StepAndroidValidation) Struct() *TStep {
return s.step
}
func (s *StepAndroidValidation) Run(r *SessionRunner) (*StepResult, error) {
return runStepAndroid(r, s.step)
}
func runStepAndroid(s *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
stepResult = &StepResult{
Name: step.Name,
StepType: stepTypeAndroid,
Success: false,
ContentSize: 0,
}
screenshots := make([]string, 0)
// override step variables
stepVariables, err := s.MergeStepVariables(step.Variables)
if err != nil {
return
}
parser := s.GetParser()
// parse device serial
serial := step.Android.AndroidDevice.SerialNumber
if serial != "" {
sn, err := parser.ParseString(serial, stepVariables)
if err != nil {
return stepResult, err
}
serial = sn.(string)
}
// get uiaClient driver
uiaClient, ok := s.hrpRunner.uiClients[serial]
if !ok {
err = fmt.Errorf("uia client not found for device %s", serial)
return
}
uiaClient.StartTime = s.startTime
defer func() {
attachments := make(map[string]interface{})
if err != nil {
attachments["error"] = err.Error()
}
// save attachments
screenshots = append(screenshots, uiaClient.ScreenShots...)
attachments["screenshots"] = screenshots
stepResult.Attachments = attachments
// update summary
s.summary.Records = append(s.summary.Records, stepResult)
s.summary.Stat.Total += 1
if stepResult.Success {
s.summary.Stat.Successes += 1
} else {
s.summary.Stat.Failures += 1
// update summary result to failed
s.summary.Success = false
}
}()
// prepare actions
var actions []uixt.MobileAction
if step.Android.Actions == nil {
actions = []uixt.MobileAction{
{
Method: step.Android.Method,
Params: step.Android.Params,
},
}
} else {
actions = step.Android.Actions
}
// run actions
for _, action := range actions {
if action.Params, err = parser.Parse(action.Params, stepVariables); err != nil {
return stepResult, errors.Wrap(err, "parse action params failed")
}
if err := uiaClient.DoAction(action); err != nil {
return stepResult, err
}
}
// take snapshot
screenshotPath, err := uiaClient.ScreenShot(
fmt.Sprintf("%d_validate_%d", uiaClient.StartTime.Unix(), time.Now().Unix()))
if err != nil {
log.Warn().Err(err).Str("step", step.Name).Msg("take screenshot failed")
} else {
log.Info().Str("path", screenshotPath).Msg("take screenshot before validation")
screenshots = append(screenshots, screenshotPath)
}
// validate
validateResults, err := validateUI(uiaClient, step.Validators)
if err != nil {
return
}
sessionData := newSessionData()
sessionData.Validators = validateResults
stepResult.Data = sessionData
stepResult.Success = true
return stepResult, nil
}
-26
View File
@@ -1,26 +0,0 @@
//go:build localtest
package hrp
import (
"testing"
)
func TestAndroidAction(t *testing.T) {
testCase := &TestCase{
Config: NewConfig("android ui action"),
TestSteps: []IStep{
NewStep("launch douyin").
Android().Serial("xxx").Tap("抖音").
Validate().
AssertNameExists("首页", "首页 tab 不存在").
AssertNameExists("消息", "消息 tab 不存在"),
NewStep("swipe up and down").
Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(),
},
}
err := NewRunner(t).Run(testCase)
if err != nil {
t.Fatal(err)
}
}
+174 -133
View File
@@ -4,12 +4,12 @@ import (
"fmt" "fmt"
"time" "time"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/pkg/uixt"
) )
// ios setting options
var ( var (
WithUDID = uixt.WithUDID WithUDID = uixt.WithUDID
WithWDAPort = uixt.WithWDAPort WithWDAPort = uixt.WithWDAPort
@@ -22,64 +22,79 @@ var (
WithPerfOptions = uixt.WithPerfOptions WithPerfOptions = uixt.WithPerfOptions
) )
type IOSStep struct { // android setting options
uixt.IOSDevice `yaml:",inline"` // inline refers to https://pkg.go.dev/gopkg.in/yaml.v3#Marshal var (
WithSerialNumber = uixt.WithSerialNumber
WithAdbIP = uixt.WithAdbIP
WithAdbPort = uixt.WithAdbPort
WithAdbLogOn = uixt.WithAdbLogOn
)
type MobileStep struct {
Serial string `json:"serial,omitempty" yaml:"serial,omitempty"`
uixt.MobileAction `yaml:",inline"` uixt.MobileAction `yaml:",inline"`
Actions []uixt.MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"` Actions []uixt.MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"`
} }
// StepIOS implements IStep interface. // StepMobile implements IStep interface.
type StepIOS struct { type StepMobile struct {
step *TStep step *TStep
} }
func (s *StepIOS) UDID(udid string) *StepIOS { func (s *StepMobile) mobileStep() *MobileStep {
s.step.IOS.UDID = udid if s.step.IOS != nil {
return &StepIOS{step: s.step} return s.step.IOS
}
return s.step.Android
} }
func (s *StepIOS) InstallApp(path string) *StepIOS { func (s *StepMobile) Serial(serial string) *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Serial = serial
return &StepMobile{step: s.step}
}
func (s *StepMobile) InstallApp(path string) *StepMobile {
s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.AppInstall, Method: uixt.AppInstall,
Params: path, Params: path,
}) })
return s return s
} }
func (s *StepIOS) AppLaunch(bundleId string) *StepIOS { func (s *StepMobile) AppLaunch(bundleId string) *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.AppLaunch, Method: uixt.AppLaunch,
Params: bundleId, Params: bundleId,
}) })
return s return s
} }
func (s *StepIOS) AppLaunchUnattached(bundleId string) *StepIOS { func (s *StepMobile) AppLaunchUnattached(bundleId string) *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.AppLaunchUnattached, Method: uixt.AppLaunchUnattached,
Params: bundleId, Params: bundleId,
}) })
return s return s
} }
func (s *StepIOS) AppTerminate(bundleId string) *StepIOS { func (s *StepMobile) AppTerminate(bundleId string) *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.AppTerminate, Method: uixt.AppTerminate,
Params: bundleId, Params: bundleId,
}) })
return s return s
} }
func (s *StepIOS) Home() *StepIOS { func (s *StepMobile) Home() *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.ACTION_Home, Method: uixt.ACTION_Home,
Params: nil, Params: nil,
}) })
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// TapXY taps the point {X,Y}, X & Y is percentage of coordinates // TapXY taps the point {X,Y}, X & Y is percentage of coordinates
func (s *StepIOS) TapXY(x, y float64, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) TapXY(x, y float64, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_TapXY, Method: uixt.ACTION_TapXY,
Params: []float64{x, y}, Params: []float64{x, y},
@@ -87,12 +102,12 @@ func (s *StepIOS) TapXY(x, y float64, options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// TapAbsXY taps the point {X,Y}, X & Y is absolute coordinates // TapAbsXY taps the point {X,Y}, X & Y is absolute coordinates
func (s *StepIOS) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_TapAbsXY, Method: uixt.ACTION_TapAbsXY,
Params: []float64{x, y}, Params: []float64{x, y},
@@ -100,12 +115,12 @@ func (s *StepIOS) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepIOS
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// Tap taps on the target element // Tap taps on the target element
func (s *StepIOS) Tap(params string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) Tap(params string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Tap, Method: uixt.ACTION_Tap,
Params: params, Params: params,
@@ -113,12 +128,12 @@ func (s *StepIOS) Tap(params string, options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// Tap taps on the target element by OCR recognition // Tap taps on the target element by OCR recognition
func (s *StepIOS) TapByOCR(ocrText string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) TapByOCR(ocrText string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_TapByOCR, Method: uixt.ACTION_TapByOCR,
Params: ocrText, Params: ocrText,
@@ -126,12 +141,12 @@ func (s *StepIOS) TapByOCR(ocrText string, options ...uixt.ActionOption) *StepIO
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// Tap taps on the target element by CV recognition // Tap taps on the target element by CV recognition
func (s *StepIOS) TapByCV(imagePath string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) TapByCV(imagePath string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_TapByCV, Method: uixt.ACTION_TapByCV,
Params: imagePath, Params: imagePath,
@@ -139,20 +154,20 @@ func (s *StepIOS) TapByCV(imagePath string, options ...uixt.ActionOption) *StepI
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// DoubleTapXY double taps the point {X,Y}, X & Y is percentage of coordinates // DoubleTapXY double taps the point {X,Y}, X & Y is percentage of coordinates
func (s *StepIOS) DoubleTapXY(x, y float64) *StepIOS { func (s *StepMobile) DoubleTapXY(x, y float64) *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.ACTION_DoubleTapXY, Method: uixt.ACTION_DoubleTapXY,
Params: []float64{x, y}, Params: []float64{x, y},
}) })
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) DoubleTap(params string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) DoubleTap(params string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_DoubleTap, Method: uixt.ACTION_DoubleTap,
Params: params, Params: params,
@@ -160,11 +175,11 @@ func (s *StepIOS) DoubleTap(params string, options ...uixt.ActionOption) *StepIO
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Swipe, Method: uixt.ACTION_Swipe,
Params: []float64{sx, sy, ex, ey}, Params: []float64{sx, sy, ex, ey},
@@ -172,11 +187,11 @@ func (s *StepIOS) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *S
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeUp(options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeUp(options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Swipe, Method: uixt.ACTION_Swipe,
Params: "up", Params: "up",
@@ -184,11 +199,11 @@ func (s *StepIOS) SwipeUp(options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeDown(options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeDown(options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Swipe, Method: uixt.ACTION_Swipe,
Params: "down", Params: "down",
@@ -196,11 +211,11 @@ func (s *StepIOS) SwipeDown(options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeLeft(options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeLeft(options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Swipe, Method: uixt.ACTION_Swipe,
Params: "left", Params: "left",
@@ -208,11 +223,11 @@ func (s *StepIOS) SwipeLeft(options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeRight(options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeRight(options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Swipe, Method: uixt.ACTION_Swipe,
Params: "right", Params: "right",
@@ -220,11 +235,11 @@ func (s *StepIOS) SwipeRight(options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeToTapApp(appName string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeToTapApp(appName string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapApp, Method: uixt.ACTION_SwipeToTapApp,
Params: appName, Params: appName,
@@ -232,11 +247,11 @@ func (s *StepIOS) SwipeToTapApp(appName string, options ...uixt.ActionOption) *S
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeToTapText(text string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeToTapText(text string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapText, Method: uixt.ACTION_SwipeToTapText,
Params: text, Params: text,
@@ -244,11 +259,11 @@ func (s *StepIOS) SwipeToTapText(text string, options ...uixt.ActionOption) *Ste
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) SwipeToTapTexts(texts []string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) SwipeToTapTexts(texts []string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_SwipeToTapTexts, Method: uixt.ACTION_SwipeToTapTexts,
Params: texts, Params: texts,
@@ -256,11 +271,11 @@ func (s *StepIOS) SwipeToTapTexts(texts []string, options ...uixt.ActionOption)
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) Input(text string, options ...uixt.ActionOption) *StepIOS { func (s *StepMobile) Input(text string, options ...uixt.ActionOption) *StepMobile {
action := uixt.MobileAction{ action := uixt.MobileAction{
Method: uixt.ACTION_Input, Method: uixt.ACTION_Input,
Params: text, Params: text,
@@ -268,93 +283,94 @@ func (s *StepIOS) Input(text string, options ...uixt.ActionOption) *StepIOS {
for _, option := range options { for _, option := range options {
option(&action) option(&action)
} }
s.step.IOS.Actions = append(s.step.IOS.Actions, action) s.mobileStep().Actions = append(s.mobileStep().Actions, action)
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// Times specify running times for run last action // Times specify running times for run last action
func (s *StepIOS) Times(n int) *StepIOS { func (s *StepMobile) Times(n int) *StepMobile {
if n <= 0 { if n <= 0 {
log.Warn().Int("n", n).Msg("times should be positive, set to 1") log.Warn().Int("n", n).Msg("times should be positive, set to 1")
n = 1 n = 1
} }
actionsTotal := len(s.step.IOS.Actions) mobileStep := s.mobileStep()
actionsTotal := len(mobileStep.Actions)
if actionsTotal == 0 { if actionsTotal == 0 {
return s return s
} }
// actionsTotal >=1 && n >= 1 // actionsTotal >=1 && n >= 1
lastAction := s.step.IOS.Actions[actionsTotal-1 : actionsTotal][0] lastAction := mobileStep.Actions[actionsTotal-1 : actionsTotal][0]
for i := 0; i < n-1; i++ { for i := 0; i < n-1; i++ {
// duplicate last action n-1 times // duplicate last action n-1 times
s.step.IOS.Actions = append(s.step.IOS.Actions, lastAction) mobileStep.Actions = append(mobileStep.Actions, lastAction)
} }
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// Sleep specify sleep seconds after last action // Sleep specify sleep seconds after last action
func (s *StepIOS) Sleep(n float64) *StepIOS { func (s *StepMobile) Sleep(n float64) *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.CtlSleep, Method: uixt.CtlSleep,
Params: n, Params: n,
}) })
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) ScreenShot() *StepIOS { func (s *StepMobile) ScreenShot() *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.CtlScreenShot, Method: uixt.CtlScreenShot,
Params: nil, Params: nil,
}) })
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) StartCamera() *StepIOS { func (s *StepMobile) StartCamera() *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.CtlStartCamera, Method: uixt.CtlStartCamera,
Params: nil, Params: nil,
}) })
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
func (s *StepIOS) StopCamera() *StepIOS { func (s *StepMobile) StopCamera() *StepMobile {
s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.CtlStopCamera, Method: uixt.CtlStopCamera,
Params: nil, Params: nil,
}) })
return &StepIOS{step: s.step} return &StepMobile{step: s.step}
} }
// Validate switches to step validation. // Validate switches to step validation.
func (s *StepIOS) Validate() *StepIOSValidation { func (s *StepMobile) Validate() *StepMobileUIValidation {
return &StepIOSValidation{ return &StepMobileUIValidation{
step: s.step, step: s.step,
} }
} }
func (s *StepIOS) Name() string { func (s *StepMobile) Name() string {
return s.step.Name return s.step.Name
} }
func (s *StepIOS) Type() StepType { func (s *StepMobile) Type() StepType {
return stepTypeIOS return stepTypeIOS
} }
func (s *StepIOS) Struct() *TStep { func (s *StepMobile) Struct() *TStep {
return s.step return s.step
} }
func (s *StepIOS) Run(r *SessionRunner) (*StepResult, error) { func (s *StepMobile) Run(r *SessionRunner) (*StepResult, error) {
return runStepIOS(r, s.step) return runStepMobileUI(r, s.step)
} }
// StepIOSValidation implements IStep interface. // StepMobileUIValidation implements IStep interface.
type StepIOSValidation struct { type StepMobileUIValidation struct {
step *TStep step *TStep
} }
func (s *StepIOSValidation) AssertNameExists(expectedName string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertNameExists(expectedName string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorName, Check: uixt.SelectorName,
Assert: uixt.AssertionExists, Assert: uixt.AssertionExists,
@@ -369,7 +385,7 @@ func (s *StepIOSValidation) AssertNameExists(expectedName string, msg ...string)
return s return s
} }
func (s *StepIOSValidation) AssertNameNotExists(expectedName string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertNameNotExists(expectedName string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorName, Check: uixt.SelectorName,
Assert: uixt.AssertionNotExists, Assert: uixt.AssertionNotExists,
@@ -384,7 +400,7 @@ func (s *StepIOSValidation) AssertNameNotExists(expectedName string, msg ...stri
return s return s
} }
func (s *StepIOSValidation) AssertLabelExists(expectedLabel string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertLabelExists(expectedLabel string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorLabel, Check: uixt.SelectorLabel,
Assert: uixt.AssertionExists, Assert: uixt.AssertionExists,
@@ -399,7 +415,7 @@ func (s *StepIOSValidation) AssertLabelExists(expectedLabel string, msg ...strin
return s return s
} }
func (s *StepIOSValidation) AssertLabelNotExists(expectedLabel string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertLabelNotExists(expectedLabel string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorLabel, Check: uixt.SelectorLabel,
Assert: uixt.AssertionNotExists, Assert: uixt.AssertionNotExists,
@@ -414,7 +430,7 @@ func (s *StepIOSValidation) AssertLabelNotExists(expectedLabel string, msg ...st
return s return s
} }
func (s *StepIOSValidation) AssertOCRExists(expectedText string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertOCRExists(expectedText string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorOCR, Check: uixt.SelectorOCR,
Assert: uixt.AssertionExists, Assert: uixt.AssertionExists,
@@ -429,7 +445,7 @@ func (s *StepIOSValidation) AssertOCRExists(expectedText string, msg ...string)
return s return s
} }
func (s *StepIOSValidation) AssertOCRNotExists(expectedText string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertOCRNotExists(expectedText string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorOCR, Check: uixt.SelectorOCR,
Assert: uixt.AssertionNotExists, Assert: uixt.AssertionNotExists,
@@ -444,7 +460,7 @@ func (s *StepIOSValidation) AssertOCRNotExists(expectedText string, msg ...strin
return s return s
} }
func (s *StepIOSValidation) AssertImageExists(expectedImagePath string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertImageExists(expectedImagePath string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorImage, Check: uixt.SelectorImage,
Assert: uixt.AssertionExists, Assert: uixt.AssertionExists,
@@ -459,7 +475,7 @@ func (s *StepIOSValidation) AssertImageExists(expectedImagePath string, msg ...s
return s return s
} }
func (s *StepIOSValidation) AssertImageNotExists(expectedImagePath string, msg ...string) *StepIOSValidation { func (s *StepMobileUIValidation) AssertImageNotExists(expectedImagePath string, msg ...string) *StepMobileUIValidation {
v := Validator{ v := Validator{
Check: uixt.SelectorImage, Check: uixt.SelectorImage,
Assert: uixt.AssertionNotExists, Assert: uixt.AssertionNotExists,
@@ -474,39 +490,62 @@ func (s *StepIOSValidation) AssertImageNotExists(expectedImagePath string, msg .
return s return s
} }
func (s *StepIOSValidation) Name() string { func (s *StepMobileUIValidation) Name() string {
return s.step.Name return s.step.Name
} }
func (s *StepIOSValidation) Type() StepType { func (s *StepMobileUIValidation) Type() StepType {
return stepTypeIOS return stepTypeIOS
} }
func (s *StepIOSValidation) Struct() *TStep { func (s *StepMobileUIValidation) Struct() *TStep {
return s.step return s.step
} }
func (s *StepIOSValidation) Run(r *SessionRunner) (*StepResult, error) { func (s *StepMobileUIValidation) Run(r *SessionRunner) (*StepResult, error) {
return runStepIOS(r, s.step) return runStepMobileUI(r, s.step)
} }
func (r *HRPRunner) getUIDriver(uuid string) (client *uixt.DriverExt, err error) { func (r *HRPRunner) initUIClient(uuid string, osType string) (client *uixt.DriverExt, err error) {
// avoid duplicate init
if uuid == "" && len(r.uiClients) > 0 { if uuid == "" && len(r.uiClients) > 0 {
for _, v := range r.uiClients { for _, v := range r.uiClients {
return v, nil return v, nil
} }
} }
client, ok := r.uiClients[uuid] // avoid duplicate init
if !ok { if uuid != "" {
err = fmt.Errorf("driver not found for device %s", uuid) if client, ok := r.uiClients[uuid]; ok {
return return client, nil
}
} }
var device uixt.Device
if osType == "ios" {
device, err = uixt.NewIOSDevice(uixt.WithUDID(uuid))
} else {
device, err = uixt.NewAndroidDevice(uixt.WithSerialNumber(uuid))
}
if err != nil {
return nil, errors.Wrapf(err, "init %s device failed", osType)
}
client, err = device.NewDriver(nil)
if err != nil {
return nil, err
}
// cache wda client
if r.uiClients == nil {
r.uiClients = make(map[string]*uixt.DriverExt)
}
r.uiClients[client.UUID] = client
return client, nil return client, nil
} }
func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err error) { func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err error) {
stepResult = &StepResult{ stepResult = &StepResult{
Name: step.Name, Name: step.Name,
StepType: stepTypeIOS, StepType: stepTypeIOS,
@@ -522,22 +561,24 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro
} }
parser := s.GetParser() parser := s.GetParser()
// parse device udid var osType string
udid := step.IOS.IOSDevice.UDID var mobileStep *MobileStep
if udid != "" { if step.IOS != nil {
sn, err := parser.ParseString(udid, stepVariables) // ios step
if err != nil { osType = "ios"
return stepResult, err mobileStep = step.IOS
} } else {
udid = sn.(string) // android step
osType = "android"
mobileStep = step.Android
} }
// init wdaClient driver // init wda/uia driver
wdaClient, err := s.hrpRunner.getUIDriver(udid) uiDriver, err := s.hrpRunner.initUIClient(mobileStep.Serial, osType)
if err != nil { if err != nil {
return return
} }
wdaClient.StartTime = s.startTime uiDriver.StartTime = s.startTime
defer func() { defer func() {
attachments := make(map[string]interface{}) attachments := make(map[string]interface{})
@@ -546,7 +587,7 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro
} }
// save attachments // save attachments
screenshots = append(screenshots, wdaClient.ScreenShots...) screenshots = append(screenshots, uiDriver.ScreenShots...)
attachments["screenshots"] = screenshots attachments["screenshots"] = screenshots
stepResult.Attachments = attachments stepResult.Attachments = attachments
@@ -564,15 +605,15 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro
// prepare actions // prepare actions
var actions []uixt.MobileAction var actions []uixt.MobileAction
if step.IOS.Actions == nil { if mobileStep.Actions == nil {
actions = []uixt.MobileAction{ actions = []uixt.MobileAction{
{ {
Method: step.IOS.Method, Method: mobileStep.Method,
Params: step.IOS.Params, Params: mobileStep.Params,
}, },
} }
} else { } else {
actions = step.IOS.Actions actions = mobileStep.Actions
} }
// run actions // run actions
@@ -580,14 +621,14 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro
if action.Params, err = parser.Parse(action.Params, stepVariables); err != nil { if action.Params, err = parser.Parse(action.Params, stepVariables); err != nil {
return stepResult, errors.Wrap(err, "parse action params failed") return stepResult, errors.Wrap(err, "parse action params failed")
} }
if err := wdaClient.DoAction(action); err != nil { if err := uiDriver.DoAction(action); err != nil {
return stepResult, err return stepResult, err
} }
} }
// take snapshot // take snapshot
screenshotPath, err := wdaClient.ScreenShot( screenshotPath, err := uiDriver.ScreenShot(
fmt.Sprintf("%d_validate_%d", wdaClient.StartTime.Unix(), time.Now().Unix())) fmt.Sprintf("%d_validate_%d", uiDriver.StartTime.Unix(), time.Now().Unix()))
if err != nil { if err != nil {
log.Warn().Err(err).Str("step", step.Name).Msg("take screenshot failed") log.Warn().Err(err).Str("step", step.Name).Msg("take screenshot failed")
} else { } else {
@@ -596,7 +637,7 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro
} }
// validate // validate
validateResults, err := validateUI(wdaClient, step.Validators) validateResults, err := validateUI(uiDriver, step.Validators)
if err != nil { if err != nil {
return return
} }
@@ -8,7 +8,8 @@ import (
func TestIOSSettingsAction(t *testing.T) { func TestIOSSettingsAction(t *testing.T) {
testCase := &TestCase{ testCase := &TestCase{
Config: NewConfig("ios ui action on Settings"), Config: NewConfig("ios ui action on Settings").
SetIOS(WithWDAPort(8700), WithWDAMjpegPort(8800)),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("launch Settings"). NewStep("launch Settings").
IOS().Home().Tap("设置"). IOS().Home().Tap("设置").
@@ -47,7 +48,7 @@ func TestIOSSearchApp(t *testing.T) {
func TestIOSAppLaunch(t *testing.T) { func TestIOSAppLaunch(t *testing.T) {
testCase := &TestCase{ testCase := &TestCase{
Config: NewConfig("启动 & 关闭 App"). Config: NewConfig("启动 & 关闭 App").
SetIOS(WithWDAPort(8100), WithWDAMjpegPort(9100)), SetIOS(WithWDAPort(8700), WithWDAMjpegPort(8800)),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("终止今日头条"). NewStep("终止今日头条").
IOS().AppTerminate("com.ss.iphone.article.News"), IOS().AppTerminate("com.ss.iphone.article.News"),
@@ -161,3 +162,22 @@ func TestIOSDouyinAction(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestAndroidAction(t *testing.T) {
testCase := &TestCase{
Config: NewConfig("android ui action"),
TestSteps: []IStep{
NewStep("launch douyin").
Android().Serial("xxx").Tap("抖音").
Validate().
AssertNameExists("首页", "首页 tab 不存在").
AssertNameExists("消息", "消息 tab 不存在"),
NewStep("swipe up and down").
Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(),
},
}
err := NewRunner(t).Run(testCase)
if err != nil {
t.Fatal(err)
}
}
+6 -6
View File
@@ -763,17 +763,17 @@ func (s *StepRequest) WebSocket() *StepWebSocket {
} }
// Android creates a new android action // Android creates a new android action
func (s *StepRequest) Android() *StepAndroid { func (s *StepRequest) Android() *StepMobile {
s.step.Android = &AndroidStep{} s.step.Android = &MobileStep{}
return &StepAndroid{ return &StepMobile{
step: s.step, step: s.step,
} }
} }
// IOS creates a new ios action // IOS creates a new ios action
func (s *StepRequest) IOS() *StepIOS { func (s *StepRequest) IOS() *StepMobile {
s.step.IOS = &IOSStep{} s.step.IOS = &MobileStep{}
return &StepIOS{ return &StepMobile{
step: s.step, step: s.step,
} }
} }
+2 -2
View File
@@ -261,11 +261,11 @@ func (tc *TCase) toTestCase() (*TestCase, error) {
step: step, step: step,
}) })
} else if step.IOS != nil { } else if step.IOS != nil {
testCase.TestSteps = append(testCase.TestSteps, &StepIOS{ testCase.TestSteps = append(testCase.TestSteps, &StepMobile{
step: step, step: step,
}) })
} else if step.Android != nil { } else if step.Android != nil {
testCase.TestSteps = append(testCase.TestSteps, &StepAndroid{ testCase.TestSteps = append(testCase.TestSteps, &StepMobile{
step: step, step: step,
}) })
} else { } else {