From ea311c1882ee48cd42bdee4b655df2b697052e14 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Tue, 26 Jul 2022 17:39:48 +0800 Subject: [PATCH] feat: create scaffold for mobile UI test --- hrp/step.go | 26 ++++++ hrp/step_android_ui.go | 199 +++++++++++++++++++++++++++++++++++++++++ hrp/step_ios_ui.go | 159 ++++++++++++++++++++++++++++++++ hrp/step_request.go | 16 ++++ hrp/step_ui_test.go | 44 +++++++++ 5 files changed, 444 insertions(+) create mode 100644 hrp/step_android_ui.go create mode 100644 hrp/step_ios_ui.go create mode 100644 hrp/step_ui_test.go diff --git a/hrp/step.go b/hrp/step.go index b4583852..0097c6bb 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -10,8 +10,32 @@ const ( stepTypeRendezvous StepType = "rendezvous" stepTypeThinkTime StepType = "thinktime" stepTypeWebSocket StepType = "websocket" + stepTypeAndroid StepType = "android" + stepTypeIOS StepType = "ios" ) +type MobileMethod string + +const ( + appInstall MobileMethod = "install" + appStart MobileMethod = "app_start" + cameraStart MobileMethod = "camera_start" + cameraStop MobileMethod = "camera_stop" + recordStart MobileMethod = "record_start" + recordStop MobileMethod = "record_stop" + uiClick MobileMethod = "click" + uiDoubleClick MobileMethod = "double_click" + uiLongClick MobileMethod = "long_click" + uiSwipe MobileMethod = "swipe" + uiInput MobileMethod = "input" + appClick MobileMethod = "app_click" +) + +type MobileAction struct { + Method MobileMethod `json:"method" yaml:"method"` + Params interface{} `json:"params,omitempty" yaml:"params,omitempty"` +} + type StepResult struct { Name string `json:"name" yaml:"name"` // step name StepType StepType `json:"step_type" yaml:"step_type"` // step type, testcase/request/transaction/rendezvous @@ -35,6 +59,8 @@ type TStep struct { Rendezvous *Rendezvous `json:"rendezvous,omitempty" yaml:"rendezvous,omitempty"` ThinkTime *ThinkTime `json:"think_time,omitempty" yaml:"think_time,omitempty"` WebSocket *WebSocketAction `json:"websocket,omitempty" yaml:"websocket,omitempty"` + Android *AndroidAction `json:"android,omitempty" yaml:"android,omitempty"` + IOS *IOSAction `json:"ios,omitempty" yaml:"ios,omitempty"` 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 new file mode 100644 index 00000000..9aa851f7 --- /dev/null +++ b/hrp/step_android_ui.go @@ -0,0 +1,199 @@ +package hrp + +type AndroidAction struct { + MobileAction + Serial string `json:"serial,omitempty" yaml:"serial,omitempty"` + Actions []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.Serial = serial + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) InstallApp(path string) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: appInstall, + Params: path, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) StartAppByIntent(activity string) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: appStart, + Params: activity, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) StartCamera() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: cameraStart, + Params: nil, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) StopCamera() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: cameraStop, + Params: nil, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) StartRecording() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: recordStart, + Params: nil, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) StopRecording() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: recordStop, + Params: nil, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) Click(params interface{}) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiClick, + Params: params, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) DoubleClick(params interface{}) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiDoubleClick, + Params: params, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) LongClick(params interface{}) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiLongClick, + Params: params, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) Swipe(sx, sy, ex, ey int) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiSwipe, + Params: []int{sx, sy, ex, ey}, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) SwipeUp() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiSwipe, + Params: "up", + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) SwipeDown() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiSwipe, + Params: "down", + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) SwipeLeft() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiSwipe, + Params: "left", + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) SwipeRight() *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiSwipe, + Params: "right", + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) Input(text string) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: uiInput, + Params: text, + }) + return &StepAndroid{step: s.step} +} + +func (s *StepAndroid) StartAppByClick(name string) *StepAndroid { + s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ + Method: appClick, + Params: name, + }) + 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) 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(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) { + stepResult = &StepResult{ + Name: step.Name, + StepType: stepTypeAndroid, + Success: false, + ContentSize: 0, + } + return stepResult, nil +} diff --git a/hrp/step_ios_ui.go b/hrp/step_ios_ui.go new file mode 100644 index 00000000..b6903e80 --- /dev/null +++ b/hrp/step_ios_ui.go @@ -0,0 +1,159 @@ +package hrp + +type IOSAction struct { + MobileAction + UDID string `json:"udid,omitempty" yaml:"udid,omitempty"` + Actions []MobileAction `json:"actions,omitempty" yaml:"actions,omitempty"` +} + +// StepIOS implements IStep interface. +type StepIOS struct { + step *TStep +} + +func (s *StepIOS) UDID(udid string) *StepIOS { + s.step.IOS.UDID = udid + return &StepIOS{step: s.step} +} + +func (s *StepIOS) InstallApp(path string) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: appInstall, + Params: path, + }) + return s +} + +func (s *StepIOS) Click(params interface{}) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiClick, + Params: params, + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) DoubleClick(params interface{}) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiDoubleClick, + Params: params, + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) LongClick(params interface{}) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiLongClick, + Params: params, + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) Swipe(sx, sy, ex, ey int) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiSwipe, + Params: []int{sx, sy, ex, ey}, + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) SwipeUp() *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiSwipe, + Params: "up", + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) SwipeDown() *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiSwipe, + Params: "down", + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) SwipeLeft() *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiSwipe, + Params: "left", + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) SwipeRight() *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiSwipe, + Params: "right", + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) Input(text string) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiInput, + Params: text, + }) + return &StepIOS{step: s.step} +} + +func (s *StepIOS) StartAppByClick(name string) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: appClick, + Params: name, + }) + return &StepIOS{step: s.step} +} + +// Validate switches to step validation. +func (s *StepIOS) Validate() *StepIOSValidation { + return &StepIOSValidation{ + step: s.step, + } +} + +func (s *StepIOS) Name() string { + return s.step.Name +} + +func (s *StepIOS) Type() StepType { + return stepTypeAndroid +} + +func (s *StepIOS) Struct() *TStep { + return s.step +} + +func (s *StepIOS) Run(r *SessionRunner) (*StepResult, error) { + return runStepIOS(r, s.step) +} + +// StepIOSValidation implements IStep interface. +type StepIOSValidation struct { + step *TStep +} + +func (s *StepIOSValidation) Name() string { + return s.step.Name +} + +func (s *StepIOSValidation) Type() StepType { + return stepTypeAndroid +} + +func (s *StepIOSValidation) Struct() *TStep { + return s.step +} + +func (s *StepIOSValidation) Run(r *SessionRunner) (*StepResult, error) { + return runStepIOS(r, s.step) +} + +func runStepIOS(r *SessionRunner, step *TStep) (stepResult *StepResult, err error) { + stepResult = &StepResult{ + Name: step.Name, + StepType: stepTypeAndroid, + Success: false, + ContentSize: 0, + } + return stepResult, nil +} diff --git a/hrp/step_request.go b/hrp/step_request.go index 651ce8fe..f2669236 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -759,6 +759,22 @@ func (s *StepRequest) WebSocket() *StepWebSocket { } } +// Android creates a new android action +func (s *StepRequest) Android() *StepAndroid { + s.step.Android = &AndroidAction{} + return &StepAndroid{ + step: s.step, + } +} + +// IOS creates a new ios action +func (s *StepRequest) IOS() *StepIOS { + s.step.IOS = &IOSAction{} + return &StepIOS{ + step: s.step, + } +} + // StepRequestWithOptionalArgs implements IStep interface. type StepRequestWithOptionalArgs struct { step *TStep diff --git a/hrp/step_ui_test.go b/hrp/step_ui_test.go new file mode 100644 index 00000000..91d730a9 --- /dev/null +++ b/hrp/step_ui_test.go @@ -0,0 +1,44 @@ +package hrp + +import ( + "fmt" + "testing" +) + +func TestAndroidAction(t *testing.T) { + testCase := &TestCase{ + Config: NewConfig("android ui action"), + TestSteps: []IStep{ + NewStep("launch douyin"). + Android().Serial("xxx").Click("抖音"), + NewStep("swipe up and down"). + Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(), + }, + } + tCase := testCase.ToTCase() + fmt.Println(tCase) + + err := NewRunner(t).Run(testCase) + if err != nil { + t.Fatal(err) + } +} + +func TestIOSAction(t *testing.T) { + testCase := &TestCase{ + Config: NewConfig("ios ui action"), + TestSteps: []IStep{ + NewStep("launch douyin"). + IOS().UDID("xxx").Click("抖音"), + NewStep("swipe up and down"). + IOS().UDID("xxx").SwipeUp().SwipeUp().SwipeDown(), + }, + } + tCase := testCase.ToTCase() + fmt.Println(tCase) + + err := NewRunner(t).Run(testCase) + if err != nil { + t.Fatal(err) + } +}