refactor: run step with specified times

This commit is contained in:
debugtalk
2022-12-13 21:30:05 +08:00
parent 3618db1e7d
commit 839f94082a
5 changed files with 113 additions and 37 deletions

View File

@@ -3,9 +3,12 @@
package main package main
import ( import (
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/httprunner/httprunner/v4/hrp"
) )
func TestConvertTimeToSeconds(t *testing.T) { func TestConvertTimeToSeconds(t *testing.T) {
@@ -44,3 +47,70 @@ func TestMainAndroid(t *testing.T) {
wc.EnterLive(bundleID) wc.EnterLive(bundleID)
wc.Start() wc.Start()
} }
func init() {
os.Setenv("UDID", "00008030-00194DA421C1802E")
}
func TestIOSDouyinWorldCupLive(t *testing.T) {
testCase := &hrp.TestCase{
Config: hrp.NewConfig("直播_抖音_世界杯_ios").
WithVariables(map[string]interface{}{
"device": "${ENV(UDID)}",
}).
SetIOS(
hrp.WithUDID("$device"),
hrp.WithLogOn(true),
hrp.WithWDAPort(8700),
hrp.WithWDAMjpegPort(8800),
hrp.WithXCTest("com.gtf.wda.runner.xctrunner"),
),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").
IOS().
Home().
AppTerminate("com.ss.iphone.ugc.Aweme"). // 关闭已运行的抖音
AppLaunch("com.ss.iphone.ugc.Aweme").
Validate().
AssertOCRExists("首页", "抖音启动失败,「首页」不存在"),
hrp.NewStep("处理青少年弹窗").
IOS().
TapByOCR("我知道了", hrp.WithIgnoreNotFoundError(true)),
hrp.NewStep("点击首页").
IOS().
TapByOCR("首页", hrp.WithIndex(-1)).Sleep(5),
hrp.NewStep("点击世界杯页").
IOS().
SwipeToTapText("世界杯",
hrp.WithMaxRetryTimes(5),
hrp.WithCustomDirection(0.4, 0.07, 0.6, 0.07), // 滑动 tab从左到右解决「世界杯」被遮挡的问题
hrp.WithScope(0, 0, 1, 0.15), // 限定 tab 区域
hrp.WithWaitTime(1),
).
Swipe(0.5, 0.3, 0.5, 0.2), // 少量上划,解决「直播中」未展示的问题
hrp.NewStep("点击进入直播间").
IOS().
LoopTimes(30). // 重复执行 30 次
TapByOCR("直播中", hrp.WithIdentifier("click_live"), hrp.WithIndex(-1)).
Sleep(30).Back().Sleep(30),
hrp.NewStep("关闭抖音").
IOS().
AppTerminate("com.ss.iphone.ugc.Aweme"),
hrp.NewStep("返回主界面,并打开本地时间戳").
IOS().
Home().SwipeToTapApp("local", hrp.WithMaxRetryTimes(5)).Sleep(10).
Validate().
AssertOCRExists("16", "打开本地时间戳失败"),
},
}
if err := testCase.Dump2JSON("ios_worldcup_live_douyin_test.json"); err != nil {
t.Fatal(err)
}
runner := hrp.NewRunner(t).SetSaveTests(true)
err := runner.Run(testCase)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -872,7 +872,9 @@ func WithScreenShot(fileName ...string) DataOption {
} }
func NewDataOptions(options ...DataOption) *DataOptions { func NewDataOptions(options ...DataOption) *DataOptions {
dataOptions := &DataOptions{} dataOptions := &DataOptions{
Data: make(map[string]interface{}),
}
for _, option := range options { for _, option := range options {
option(dataOptions) option(dataOptions)
} }

View File

@@ -159,6 +159,9 @@ func GetIOSDeviceOptions(dev *IOSDevice) (deviceOptions []IOSDeviceOption) {
if dev.PerfOptions != nil { if dev.PerfOptions != nil {
deviceOptions = append(deviceOptions, WithPerfOptions(dev.perfOpitons()...)) deviceOptions = append(deviceOptions, WithPerfOptions(dev.perfOpitons()...))
} }
if dev.XCTestBundleID != "" {
deviceOptions = append(deviceOptions, WithXCTest(dev.XCTestBundleID))
}
if dev.ResetHomeOnStartup { if dev.ResetHomeOnStartup {
deviceOptions = append(deviceOptions, WithResetHomeOnStartup(true)) deviceOptions = append(deviceOptions, WithResetHomeOnStartup(true))
} }

View File

@@ -22,6 +22,7 @@ var (
WithAcceptAlertButtonSelector = uixt.WithAcceptAlertButtonSelector WithAcceptAlertButtonSelector = uixt.WithAcceptAlertButtonSelector
WithDismissAlertButtonSelector = uixt.WithDismissAlertButtonSelector WithDismissAlertButtonSelector = uixt.WithDismissAlertButtonSelector
WithPerfOptions = uixt.WithPerfOptions WithPerfOptions = uixt.WithPerfOptions
WithXCTest = uixt.WithXCTest
) )
// android setting options // android setting options
@@ -34,6 +35,7 @@ var (
type MobileStep struct { type MobileStep struct {
Serial string `json:"serial,omitempty" yaml:"serial,omitempty"` Serial string `json:"serial,omitempty" yaml:"serial,omitempty"`
Times int `json:"times,omitempty" yaml:"times,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"`
} }
@@ -301,25 +303,9 @@ func (s *StepMobile) Input(text string, options ...uixt.ActionOption) *StepMobil
return &StepMobile{step: s.step} return &StepMobile{step: s.step}
} }
// Times specify running times for run last action // LoopTimes specify running times for the current step
func (s *StepMobile) Times(n int) *StepMobile { func (s *StepMobile) LoopTimes(n int) *StepMobile {
if n <= 0 { s.mobileStep().Times = n
log.Warn().Int("n", n).Msg("times should be positive, set to 1")
n = 1
}
mobileStep := s.mobileStep()
actionsTotal := len(mobileStep.Actions)
if actionsTotal == 0 {
return s
}
// actionsTotal >=1 && n >= 1
lastAction := mobileStep.Actions[actionsTotal-1 : actionsTotal][0]
for i := 0; i < n-1; i++ {
// duplicate last action n-1 times
mobileStep.Actions = append(mobileStep.Actions, lastAction)
}
return &StepMobile{step: s.step} return &StepMobile{step: s.step}
} }
@@ -622,20 +608,33 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err
actions = mobileStep.Actions actions = mobileStep.Actions
} }
// run actions // run times
for _, action := range actions { times := mobileStep.Times
if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil { if times < 0 {
if !code.IsErrorPredefined(err) { log.Warn().Int("times", times).Msg("times should be positive, set to 1")
err = errors.Wrap(code.ParseError, times = 1
fmt.Sprintf("parse action params failed: %v", err)) } else if times == 0 {
times = 1
} else if times > 1 {
log.Info().Int("times", times).Msg("run actions with specified times")
}
// run actions with specified times
for i := 0; i < times; i++ {
for _, action := range actions {
if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil {
if !code.IsErrorPredefined(err) {
err = errors.Wrap(code.ParseError,
fmt.Sprintf("parse action params failed: %v", err))
}
return stepResult, err
} }
return stepResult, err if err := uiDriver.DoAction(action); err != nil {
} if !code.IsErrorPredefined(err) {
if err := uiDriver.DoAction(action); err != nil { err = errors.Wrap(code.MobileUIDriverError, err.Error())
if !code.IsErrorPredefined(err) { }
err = errors.Wrap(code.MobileUIDriverError, err.Error()) return stepResult, err
} }
return stepResult, err
} }
} }

View File

@@ -32,7 +32,7 @@ func TestIOSSearchApp(t *testing.T) {
Config: NewConfig("ios ui action on Search App 资源库"), Config: NewConfig("ios ui action on Search App 资源库"),
TestSteps: []IStep{ TestSteps: []IStep{
NewStep("进入 App 资源库 搜索框"). NewStep("进入 App 资源库 搜索框").
IOS().Home().SwipeLeft().Times(2).Tap("dewey-search-field"). IOS().Home().SwipeLeft().SwipeLeft().Tap("dewey-search-field").
Validate(). Validate().
AssertLabelExists("取消"), AssertLabelExists("取消"),
NewStep("搜索抖音"). NewStep("搜索抖音").
@@ -84,10 +84,10 @@ func TestIOSWeixinLive(t *testing.T) {
TapByOCR("直播"). // 通过 OCR 识别「直播」 TapByOCR("直播"). // 通过 OCR 识别「直播」
Validate(). Validate().
AssertLabelExists("直播"), AssertLabelExists("直播"),
NewStep("向上滑动 5 次"). NewStep("向上滑动 3,截图保存").
IOS(). IOS().
SwipeUp().Times(3).ScreenShot(). // 上划 3 次,截图保存 LoopTimes(3). // 整体循环 3 次
SwipeUp().Times(2).ScreenShot(), // 上划 2 次,截图保存 SwipeUp().SwipeUp().ScreenShot(), // 上划 2 次,截图保存
}, },
} }
err := NewRunner(t).Run(testCase) err := NewRunner(t).Run(testCase)
@@ -154,7 +154,9 @@ func TestIOSDouyinAction(t *testing.T) {
AssertLabelExists("首页", "首页 tab 不存在"). AssertLabelExists("首页", "首页 tab 不存在").
AssertLabelExists("消息", "消息 tab 不存在"), AssertLabelExists("消息", "消息 tab 不存在"),
NewStep("swipe up and down"). NewStep("swipe up and down").
IOS().SwipeUp().Times(3).SwipeDown(), IOS().
LoopTimes(3).
SwipeUp().SwipeDown(),
}, },
} }
err := NewRunner(t).Run(testCase) err := NewRunner(t).Run(testCase)