From c22b38f76f97adc0e4d8027f643633f60931171a Mon Sep 17 00:00:00 2001 From: debugtalk Date: Wed, 12 Oct 2022 21:07:57 +0800 Subject: [PATCH] refactor: merge ios and android style --- examples/uitest/demo_douyin_follow_live.json | 4 +- examples/uitest/demo_douyin_follow_live.yaml | 4 +- .../uitest/demo_douyin_follow_live_test.go | 4 +- hrp/runner.go | 1 - hrp/step.go | 4 +- hrp/step_android_ui.go | 603 ------------------ hrp/step_android_ui_test.go | 26 - hrp/{step_ios_ui.go => step_mobile_ui.go} | 307 +++++---- ..._ios_ui_test.go => step_mobile_ui_test.go} | 24 +- hrp/step_request.go | 12 +- hrp/testcase.go | 4 +- 11 files changed, 212 insertions(+), 781 deletions(-) delete mode 100644 hrp/step_android_ui.go delete mode 100644 hrp/step_android_ui_test.go rename hrp/{step_ios_ui.go => step_mobile_ui.go} (50%) rename hrp/{step_ios_ui_test.go => step_mobile_ui_test.go} (86%) diff --git a/examples/uitest/demo_douyin_follow_live.json b/examples/uitest/demo_douyin_follow_live.json index 4f9b411b..171ef268 100644 --- a/examples/uitest/demo_douyin_follow_live.json +++ b/examples/uitest/demo_douyin_follow_live.json @@ -6,8 +6,8 @@ }, "ios": [ { - "port": 8100, - "mjpeg_port": 9100, + "port": 8700, + "mjpeg_port": 8800, "log_on": true } ] diff --git a/examples/uitest/demo_douyin_follow_live.yaml b/examples/uitest/demo_douyin_follow_live.yaml index 454df7c8..f6c34c3f 100644 --- a/examples/uitest/demo_douyin_follow_live.yaml +++ b/examples/uitest/demo_douyin_follow_live.yaml @@ -3,8 +3,8 @@ config: variables: app_name: 抖音 ios: - - port: 8100 - mjpeg_port: 9100 + - port: 8700 + mjpeg_port: 8800 log_on: true teststeps: - name: 启动抖音 diff --git a/examples/uitest/demo_douyin_follow_live_test.go b/examples/uitest/demo_douyin_follow_live_test.go index ad8fc608..1eb42f93 100644 --- a/examples/uitest/demo_douyin_follow_live_test.go +++ b/examples/uitest/demo_douyin_follow_live_test.go @@ -16,8 +16,8 @@ func TestIOSDouyinFollowLive(t *testing.T) { }). SetIOS( hrp.WithLogOn(true), - hrp.WithWDAPort(8100), - hrp.WithWDAMjpegPort(9100), + hrp.WithWDAPort(8700), + hrp.WithWDAMjpegPort(8800), ), TestSteps: []hrp.IStep{ hrp.NewStep("启动抖音"). diff --git a/hrp/runner.go b/hrp/runner.go index 68cd00d4..5fd4a102 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -406,7 +406,6 @@ func (r *testCaseRunner) parseConfig() error { } iosDeviceConfig.UDID = udid.(string) } - device, err := uixt.NewIOSDevice(uixt.GetIOSDeviceOptions(iosDeviceConfig)...) if err != nil { return errors.Wrap(err, "init iOS device failed") diff --git a/hrp/step.go b/hrp/step.go index 98bccab7..ac7481f1 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -71,8 +71,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 *AndroidStep `json:"android,omitempty" yaml:"android,omitempty"` - IOS *IOSStep `json:"ios,omitempty" yaml:"ios,omitempty"` + Android *MobileStep `json:"android,omitempty" yaml:"android,omitempty"` + IOS *MobileStep `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 deleted file mode 100644 index 03eaac9f..00000000 --- a/hrp/step_android_ui.go +++ /dev/null @@ -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 -} diff --git a/hrp/step_android_ui_test.go b/hrp/step_android_ui_test.go deleted file mode 100644 index ad02cfe4..00000000 --- a/hrp/step_android_ui_test.go +++ /dev/null @@ -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) - } -} diff --git a/hrp/step_ios_ui.go b/hrp/step_mobile_ui.go similarity index 50% rename from hrp/step_ios_ui.go rename to hrp/step_mobile_ui.go index 257a0189..64e22f65 100644 --- a/hrp/step_ios_ui.go +++ b/hrp/step_mobile_ui.go @@ -4,12 +4,12 @@ import ( "fmt" "time" + "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" "github.com/pkg/errors" "github.com/rs/zerolog/log" - - "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" ) +// ios setting options var ( WithUDID = uixt.WithUDID WithWDAPort = uixt.WithWDAPort @@ -22,64 +22,79 @@ var ( WithPerfOptions = uixt.WithPerfOptions ) -type IOSStep struct { - uixt.IOSDevice `yaml:",inline"` // inline refers to https://pkg.go.dev/gopkg.in/yaml.v3#Marshal +// android setting options +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"` Actions []uixt.MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"` } -// StepIOS implements IStep interface. -type StepIOS struct { +// StepMobile implements IStep interface. +type StepMobile struct { step *TStep } -func (s *StepIOS) UDID(udid string) *StepIOS { - s.step.IOS.UDID = udid - return &StepIOS{step: s.step} +func (s *StepMobile) mobileStep() *MobileStep { + if s.step.IOS != nil { + return s.step.IOS + } + return s.step.Android } -func (s *StepIOS) InstallApp(path string) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) Serial(serial string) *StepMobile { + 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, Params: path, }) return s } -func (s *StepIOS) AppLaunch(bundleId string) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) AppLaunch(bundleId string) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.AppLaunch, Params: bundleId, }) return s } -func (s *StepIOS) AppLaunchUnattached(bundleId string) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) AppLaunchUnattached(bundleId string) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.AppLaunchUnattached, Params: bundleId, }) return s } -func (s *StepIOS) AppTerminate(bundleId string) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) AppTerminate(bundleId string) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.AppTerminate, Params: bundleId, }) return s } -func (s *StepIOS) Home() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) Home() *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.ACTION_Home, Params: nil, }) - return &StepIOS{step: s.step} + return &StepMobile{step: s.step} } // 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{ Method: uixt.ACTION_TapXY, Params: []float64{x, y}, @@ -87,12 +102,12 @@ func (s *StepIOS) TapXY(x, y float64, options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } // 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{ Method: uixt.ACTION_TapAbsXY, Params: []float64{x, y}, @@ -100,12 +115,12 @@ func (s *StepIOS) TapAbsXY(x, y float64, options ...uixt.ActionOption) *StepIOS for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } // 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{ Method: uixt.ACTION_Tap, Params: params, @@ -113,12 +128,12 @@ func (s *StepIOS) Tap(params string, options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } // 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{ Method: uixt.ACTION_TapByOCR, Params: ocrText, @@ -126,12 +141,12 @@ func (s *StepIOS) TapByOCR(ocrText string, options ...uixt.ActionOption) *StepIO for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } // 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{ Method: uixt.ACTION_TapByCV, Params: imagePath, @@ -139,20 +154,20 @@ func (s *StepIOS) TapByCV(imagePath string, options ...uixt.ActionOption) *StepI for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } // DoubleTapXY double taps the point {X,Y}, X & Y is percentage of coordinates -func (s *StepIOS) DoubleTapXY(x, y float64) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) DoubleTapXY(x, y float64) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.ACTION_DoubleTapXY, 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{ Method: uixt.ACTION_DoubleTap, Params: params, @@ -160,11 +175,11 @@ func (s *StepIOS) DoubleTap(params string, options ...uixt.ActionOption) *StepIO for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + 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{ Method: uixt.ACTION_Swipe, 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 { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } -func (s *StepIOS) SwipeUp(options ...uixt.ActionOption) *StepIOS { +func (s *StepMobile) SwipeUp(options ...uixt.ActionOption) *StepMobile { action := uixt.MobileAction{ Method: uixt.ACTION_Swipe, Params: "up", @@ -184,11 +199,11 @@ func (s *StepIOS) SwipeUp(options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } -func (s *StepIOS) SwipeDown(options ...uixt.ActionOption) *StepIOS { +func (s *StepMobile) SwipeDown(options ...uixt.ActionOption) *StepMobile { action := uixt.MobileAction{ Method: uixt.ACTION_Swipe, Params: "down", @@ -196,11 +211,11 @@ func (s *StepIOS) SwipeDown(options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } -func (s *StepIOS) SwipeLeft(options ...uixt.ActionOption) *StepIOS { +func (s *StepMobile) SwipeLeft(options ...uixt.ActionOption) *StepMobile { action := uixt.MobileAction{ Method: uixt.ACTION_Swipe, Params: "left", @@ -208,11 +223,11 @@ func (s *StepIOS) SwipeLeft(options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } -func (s *StepIOS) SwipeRight(options ...uixt.ActionOption) *StepIOS { +func (s *StepMobile) SwipeRight(options ...uixt.ActionOption) *StepMobile { action := uixt.MobileAction{ Method: uixt.ACTION_Swipe, Params: "right", @@ -220,11 +235,11 @@ func (s *StepIOS) SwipeRight(options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + 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{ Method: uixt.ACTION_SwipeToTapApp, Params: appName, @@ -232,11 +247,11 @@ func (s *StepIOS) SwipeToTapApp(appName string, options ...uixt.ActionOption) *S for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + 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{ Method: uixt.ACTION_SwipeToTapText, Params: text, @@ -244,11 +259,11 @@ func (s *StepIOS) SwipeToTapText(text string, options ...uixt.ActionOption) *Ste for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + 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{ Method: uixt.ACTION_SwipeToTapTexts, Params: texts, @@ -256,11 +271,11 @@ func (s *StepIOS) SwipeToTapTexts(texts []string, options ...uixt.ActionOption) for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + 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{ Method: uixt.ACTION_Input, Params: text, @@ -268,93 +283,94 @@ func (s *StepIOS) Input(text string, options ...uixt.ActionOption) *StepIOS { for _, option := range options { option(&action) } - s.step.IOS.Actions = append(s.step.IOS.Actions, action) - return &StepIOS{step: s.step} + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} } // 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 { log.Warn().Int("n", n).Msg("times should be positive, set to 1") n = 1 } - actionsTotal := len(s.step.IOS.Actions) + mobileStep := s.mobileStep() + actionsTotal := len(mobileStep.Actions) if actionsTotal == 0 { return s } // 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++ { // 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 -func (s *StepIOS) Sleep(n float64) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) Sleep(n float64) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlSleep, Params: n, }) - return &StepIOS{step: s.step} + return &StepMobile{step: s.step} } -func (s *StepIOS) ScreenShot() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) ScreenShot() *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlScreenShot, Params: nil, }) - return &StepIOS{step: s.step} + return &StepMobile{step: s.step} } -func (s *StepIOS) StartCamera() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) StartCamera() *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlStartCamera, Params: nil, }) - return &StepIOS{step: s.step} + return &StepMobile{step: s.step} } -func (s *StepIOS) StopCamera() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, uixt.MobileAction{ +func (s *StepMobile) StopCamera() *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlStopCamera, Params: nil, }) - return &StepIOS{step: s.step} + return &StepMobile{step: s.step} } // Validate switches to step validation. -func (s *StepIOS) Validate() *StepIOSValidation { - return &StepIOSValidation{ +func (s *StepMobile) Validate() *StepMobileUIValidation { + return &StepMobileUIValidation{ step: s.step, } } -func (s *StepIOS) Name() string { +func (s *StepMobile) Name() string { return s.step.Name } -func (s *StepIOS) Type() StepType { +func (s *StepMobile) Type() StepType { return stepTypeIOS } -func (s *StepIOS) Struct() *TStep { +func (s *StepMobile) Struct() *TStep { return s.step } -func (s *StepIOS) Run(r *SessionRunner) (*StepResult, error) { - return runStepIOS(r, s.step) +func (s *StepMobile) Run(r *SessionRunner) (*StepResult, error) { + return runStepMobileUI(r, s.step) } -// StepIOSValidation implements IStep interface. -type StepIOSValidation struct { +// StepMobileUIValidation implements IStep interface. +type StepMobileUIValidation struct { step *TStep } -func (s *StepIOSValidation) AssertNameExists(expectedName string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertNameExists(expectedName string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorName, Assert: uixt.AssertionExists, @@ -369,7 +385,7 @@ func (s *StepIOSValidation) AssertNameExists(expectedName string, msg ...string) return s } -func (s *StepIOSValidation) AssertNameNotExists(expectedName string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertNameNotExists(expectedName string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorName, Assert: uixt.AssertionNotExists, @@ -384,7 +400,7 @@ func (s *StepIOSValidation) AssertNameNotExists(expectedName string, msg ...stri return s } -func (s *StepIOSValidation) AssertLabelExists(expectedLabel string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertLabelExists(expectedLabel string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorLabel, Assert: uixt.AssertionExists, @@ -399,7 +415,7 @@ func (s *StepIOSValidation) AssertLabelExists(expectedLabel string, msg ...strin return s } -func (s *StepIOSValidation) AssertLabelNotExists(expectedLabel string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertLabelNotExists(expectedLabel string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorLabel, Assert: uixt.AssertionNotExists, @@ -414,7 +430,7 @@ func (s *StepIOSValidation) AssertLabelNotExists(expectedLabel string, msg ...st return s } -func (s *StepIOSValidation) AssertOCRExists(expectedText string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertOCRExists(expectedText string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorOCR, Assert: uixt.AssertionExists, @@ -429,7 +445,7 @@ func (s *StepIOSValidation) AssertOCRExists(expectedText string, msg ...string) return s } -func (s *StepIOSValidation) AssertOCRNotExists(expectedText string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertOCRNotExists(expectedText string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorOCR, Assert: uixt.AssertionNotExists, @@ -444,7 +460,7 @@ func (s *StepIOSValidation) AssertOCRNotExists(expectedText string, msg ...strin return s } -func (s *StepIOSValidation) AssertImageExists(expectedImagePath string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertImageExists(expectedImagePath string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorImage, Assert: uixt.AssertionExists, @@ -459,7 +475,7 @@ func (s *StepIOSValidation) AssertImageExists(expectedImagePath string, msg ...s return s } -func (s *StepIOSValidation) AssertImageNotExists(expectedImagePath string, msg ...string) *StepIOSValidation { +func (s *StepMobileUIValidation) AssertImageNotExists(expectedImagePath string, msg ...string) *StepMobileUIValidation { v := Validator{ Check: uixt.SelectorImage, Assert: uixt.AssertionNotExists, @@ -474,39 +490,62 @@ func (s *StepIOSValidation) AssertImageNotExists(expectedImagePath string, msg . return s } -func (s *StepIOSValidation) Name() string { +func (s *StepMobileUIValidation) Name() string { return s.step.Name } -func (s *StepIOSValidation) Type() StepType { +func (s *StepMobileUIValidation) Type() StepType { return stepTypeIOS } -func (s *StepIOSValidation) Struct() *TStep { +func (s *StepMobileUIValidation) Struct() *TStep { return s.step } -func (s *StepIOSValidation) Run(r *SessionRunner) (*StepResult, error) { - return runStepIOS(r, s.step) +func (s *StepMobileUIValidation) Run(r *SessionRunner) (*StepResult, error) { + 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 { for _, v := range r.uiClients { return v, nil } } - client, ok := r.uiClients[uuid] - if !ok { - err = fmt.Errorf("driver not found for device %s", uuid) - return + // avoid duplicate init + if uuid != "" { + if client, ok := r.uiClients[uuid]; ok { + 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 } -func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err error) { +func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err error) { stepResult = &StepResult{ Name: step.Name, StepType: stepTypeIOS, @@ -522,22 +561,24 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro } parser := s.GetParser() - // parse device udid - udid := step.IOS.IOSDevice.UDID - if udid != "" { - sn, err := parser.ParseString(udid, stepVariables) - if err != nil { - return stepResult, err - } - udid = sn.(string) + var osType string + var mobileStep *MobileStep + if step.IOS != nil { + // ios step + osType = "ios" + mobileStep = step.IOS + } else { + // android step + osType = "android" + mobileStep = step.Android } - // init wdaClient driver - wdaClient, err := s.hrpRunner.getUIDriver(udid) + // init wda/uia driver + uiDriver, err := s.hrpRunner.initUIClient(mobileStep.Serial, osType) if err != nil { return } - wdaClient.StartTime = s.startTime + uiDriver.StartTime = s.startTime defer func() { attachments := make(map[string]interface{}) @@ -546,7 +587,7 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro } // save attachments - screenshots = append(screenshots, wdaClient.ScreenShots...) + screenshots = append(screenshots, uiDriver.ScreenShots...) attachments["screenshots"] = screenshots stepResult.Attachments = attachments @@ -564,15 +605,15 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro // prepare actions var actions []uixt.MobileAction - if step.IOS.Actions == nil { + if mobileStep.Actions == nil { actions = []uixt.MobileAction{ { - Method: step.IOS.Method, - Params: step.IOS.Params, + Method: mobileStep.Method, + Params: mobileStep.Params, }, } } else { - actions = step.IOS.Actions + actions = mobileStep.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 { 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 } } // take snapshot - screenshotPath, err := wdaClient.ScreenShot( - fmt.Sprintf("%d_validate_%d", wdaClient.StartTime.Unix(), time.Now().Unix())) + screenshotPath, err := uiDriver.ScreenShot( + fmt.Sprintf("%d_validate_%d", uiDriver.StartTime.Unix(), time.Now().Unix())) if err != nil { log.Warn().Err(err).Str("step", step.Name).Msg("take screenshot failed") } else { @@ -596,7 +637,7 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro } // validate - validateResults, err := validateUI(wdaClient, step.Validators) + validateResults, err := validateUI(uiDriver, step.Validators) if err != nil { return } diff --git a/hrp/step_ios_ui_test.go b/hrp/step_mobile_ui_test.go similarity index 86% rename from hrp/step_ios_ui_test.go rename to hrp/step_mobile_ui_test.go index 0767b8d6..c9c7dae3 100644 --- a/hrp/step_ios_ui_test.go +++ b/hrp/step_mobile_ui_test.go @@ -8,7 +8,8 @@ import ( func TestIOSSettingsAction(t *testing.T) { testCase := &TestCase{ - Config: NewConfig("ios ui action on Settings"), + Config: NewConfig("ios ui action on Settings"). + SetIOS(WithWDAPort(8700), WithWDAMjpegPort(8800)), TestSteps: []IStep{ NewStep("launch Settings"). IOS().Home().Tap("设置"). @@ -47,7 +48,7 @@ func TestIOSSearchApp(t *testing.T) { func TestIOSAppLaunch(t *testing.T) { testCase := &TestCase{ Config: NewConfig("启动 & 关闭 App"). - SetIOS(WithWDAPort(8100), WithWDAMjpegPort(9100)), + SetIOS(WithWDAPort(8700), WithWDAMjpegPort(8800)), TestSteps: []IStep{ NewStep("终止今日头条"). IOS().AppTerminate("com.ss.iphone.article.News"), @@ -161,3 +162,22 @@ func TestIOSDouyinAction(t *testing.T) { 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) + } +} diff --git a/hrp/step_request.go b/hrp/step_request.go index cc656d34..e5adf26c 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -763,17 +763,17 @@ func (s *StepRequest) WebSocket() *StepWebSocket { } // Android creates a new android action -func (s *StepRequest) Android() *StepAndroid { - s.step.Android = &AndroidStep{} - return &StepAndroid{ +func (s *StepRequest) Android() *StepMobile { + s.step.Android = &MobileStep{} + return &StepMobile{ step: s.step, } } // IOS creates a new ios action -func (s *StepRequest) IOS() *StepIOS { - s.step.IOS = &IOSStep{} - return &StepIOS{ +func (s *StepRequest) IOS() *StepMobile { + s.step.IOS = &MobileStep{} + return &StepMobile{ step: s.step, } } diff --git a/hrp/testcase.go b/hrp/testcase.go index 6f4bd46b..401b179b 100644 --- a/hrp/testcase.go +++ b/hrp/testcase.go @@ -261,11 +261,11 @@ func (tc *TCase) toTestCase() (*TestCase, error) { step: step, }) } else if step.IOS != nil { - testCase.TestSteps = append(testCase.TestSteps, &StepIOS{ + testCase.TestSteps = append(testCase.TestSteps, &StepMobile{ step: step, }) } else if step.Android != nil { - testCase.TestSteps = append(testCase.TestSteps, &StepAndroid{ + testCase.TestSteps = append(testCase.TestSteps, &StepMobile{ step: step, }) } else {