mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-03 06:49:38 +08:00
feat: support new action: close_popups
This commit is contained in:
@@ -58,6 +58,7 @@ const (
|
||||
ACTION_SwipeToTapText ActionMethod = "swipe_to_tap_text" // swipe up & down to find text and tap
|
||||
ACTION_SwipeToTapTexts ActionMethod = "swipe_to_tap_texts" // swipe up & down to find text and tap
|
||||
ACTION_VideoCrawler ActionMethod = "video_crawler"
|
||||
ACTION_ClosePopups ActionMethod = "close_popups"
|
||||
)
|
||||
|
||||
type MobileAction struct {
|
||||
@@ -118,6 +119,7 @@ type ActionOptions struct {
|
||||
ScreenShotWithUpload bool `json:"screenshot_with_upload,omitempty" yaml:"screenshot_with_upload,omitempty"`
|
||||
ScreenShotWithLiveType bool `json:"screenshot_with_live_type,omitempty" yaml:"screenshot_with_live_type,omitempty"`
|
||||
ScreenShotWithUITypes []string `json:"screenshot_with_ui_types,omitempty" yaml:"screenshot_with_ui_types,omitempty"`
|
||||
ScreenShotWithClose bool `json:"screenshot_with_close,omitempty" yaml:"screenshot_with_close,omitempty"`
|
||||
}
|
||||
|
||||
func (o *ActionOptions) Options() []ActionOption {
|
||||
@@ -438,6 +440,12 @@ func WithScreenShotUITypes(uiTypes ...string) ActionOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithScreenShotClose(closeOn bool) ActionOption {
|
||||
return func(o *ActionOptions) {
|
||||
o.ScreenShotWithClose = closeOn
|
||||
}
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) ParseActionOptions(options ...ActionOption) []ActionOption {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
|
||||
@@ -632,6 +640,8 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) {
|
||||
return errors.Wrapf(err, "invalid video crawler params: %v(%T)", action.Params, action.Params)
|
||||
}
|
||||
return dExt.VideoCrawler(configs)
|
||||
case ACTION_ClosePopups:
|
||||
return dExt.ClosePopup(action.GetOptions()...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ type ScreenResult struct {
|
||||
VideoType string `json:"video_type,omitempty"` // video type: feed, live-preview or live
|
||||
Feed *FeedVideo `json:"feed,omitempty"`
|
||||
Live *LiveRoom `json:"live,omitempty"`
|
||||
Popup *CPResult `json:"popup,omitempty"`
|
||||
|
||||
SwipeStartTime int64 `json:"swipe_start_time"` // 滑动开始时间戳
|
||||
SwipeFinishTime int64 `json:"swipe_finish_time"` // 滑动结束时间戳
|
||||
@@ -255,6 +256,7 @@ func (dExt *DriverExt) GetStepCacheData() map[string]interface{} {
|
||||
"screenshot_take_elapsed": screenResult.ScreenshotTakeElapsed,
|
||||
"screenshot_cv_elapsed": screenResult.ScreenshotCVElapsed,
|
||||
"total_elapsed": screenResult.TotalElapsed,
|
||||
"popup": screenResult.Popup,
|
||||
}
|
||||
|
||||
screenResults[imagePath] = data
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/funplugin"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -434,6 +436,10 @@ type PointF struct {
|
||||
Y float64 `json:"y"`
|
||||
}
|
||||
|
||||
func (p PointF) IsOriginal() bool {
|
||||
return builtin.IsZeroFloat64(p.X) && builtin.IsZeroFloat64(p.Y)
|
||||
}
|
||||
|
||||
type Rect struct {
|
||||
Point
|
||||
Size
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/code"
|
||||
)
|
||||
|
||||
@@ -70,3 +73,75 @@ func (dExt *DriverExt) AutoPopupHandler() error {
|
||||
|
||||
return dExt.handleTextPopup(screenResult.Texts)
|
||||
}
|
||||
|
||||
// CRResult represents the result of recognized popup to close
|
||||
type CPResult struct {
|
||||
Type string `json:"type"`
|
||||
PopupArea Box `json:"popupArea"`
|
||||
CloseArea Box `json:"closeArea"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type PopupInfo struct {
|
||||
// TODO: support more types of popup report info
|
||||
// Status bool `json:"status"`
|
||||
Type string `json:"type"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
PicName string `json:"pic_name"`
|
||||
PicURL string `json:"pic_url"`
|
||||
Point PointF `json:"point"`
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) ClosePopup(options ...ActionOption) error {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
|
||||
// default to retry 5 times
|
||||
if actionOptions.MaxRetryTimes == 0 {
|
||||
options = append(options, WithMaxRetryTimes(5))
|
||||
}
|
||||
// set default swipe interval to 1 second
|
||||
if builtin.IsZeroFloat64(actionOptions.Interval) {
|
||||
options = append(options, WithInterval(1))
|
||||
}
|
||||
return dExt.ClosePopupHandler(options...)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) ClosePopupHandler(options ...ActionOption) error {
|
||||
actionOptions := NewActionOptions(options...)
|
||||
maxRetryTimes := actionOptions.MaxRetryTimes
|
||||
interval := actionOptions.Interval
|
||||
|
||||
for retryCount := 1; retryCount <= maxRetryTimes; retryCount++ {
|
||||
screenResult, err := dExt.GetScreenResult(
|
||||
WithScreenShotClose(true), WithScreenShotUpload(true))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get screen result failed for popup handler")
|
||||
}
|
||||
|
||||
// not popup, fast return
|
||||
if screenResult.Popup == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if err = dExt.tapPopupHandler(screenResult.Popup); err != nil {
|
||||
return err
|
||||
}
|
||||
// sleep for another popup (if existed) to pop
|
||||
time.Sleep(time.Duration(1000*interval) * time.Millisecond)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) tapPopupHandler(cpResult *CPResult) error {
|
||||
if cpResult == nil {
|
||||
return nil
|
||||
}
|
||||
log.Info().Str("type", cpResult.Type).Str("text", cpResult.Text).Msg("close popup")
|
||||
popupCenter := cpResult.CloseArea.Center()
|
||||
if err := dExt.TapAbsXY(popupCenter.X, popupCenter.Y); err != nil {
|
||||
log.Error().Err(err).Msg("tap popup failed")
|
||||
return errors.Wrap(code.MobileUIPopupError, err.Error())
|
||||
}
|
||||
// tap popup success
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -65,8 +65,9 @@ type ImageResult struct {
|
||||
// Media(媒体)
|
||||
// Chat(语音)
|
||||
// Event(赛事)
|
||||
LiveType string `json:"liveType"` // 直播间类型
|
||||
UIResult UIResultMap `json:"uiResult"` // 图标检测
|
||||
LiveType string `json:"liveType"` // 直播间类型
|
||||
UIResult UIResultMap `json:"uiResult"` // 图标检测
|
||||
CPResult CPResult `json:"closeResult"` // 弹窗按钮检测
|
||||
}
|
||||
|
||||
type APIResponseImage struct {
|
||||
@@ -385,6 +386,9 @@ func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *S
|
||||
LiveType: imageResult.LiveType,
|
||||
}
|
||||
}
|
||||
if !imageResult.CPResult.CloseArea.IsEmpty() && !imageResult.CPResult.CloseArea.Point.IsOriginal() {
|
||||
screenResult.Popup = &imageResult.CPResult
|
||||
}
|
||||
}
|
||||
|
||||
dExt.cacheStepData.screenResults[imagePath] = screenResult
|
||||
@@ -435,21 +439,25 @@ func getRectangleCenterPoint(rect image.Rectangle) (point PointF) {
|
||||
return point
|
||||
}
|
||||
|
||||
func getCenterPoint(point PointF, width, height float64) PointF {
|
||||
return PointF{
|
||||
X: point.X + width*0.5,
|
||||
Y: point.Y + height*0.5,
|
||||
}
|
||||
}
|
||||
|
||||
type UIResult struct {
|
||||
type Box struct {
|
||||
Point PointF `json:"point"`
|
||||
Width float64 `json:"width"`
|
||||
Height float64 `json:"height"`
|
||||
}
|
||||
|
||||
func (u UIResult) Center() PointF {
|
||||
return getCenterPoint(u.Point, u.Width, u.Height)
|
||||
func (box Box) IsEmpty() bool {
|
||||
return builtin.IsZeroFloat64(box.Width) && builtin.IsZeroFloat64(box.Height)
|
||||
}
|
||||
|
||||
func (box Box) Center() PointF {
|
||||
return PointF{
|
||||
X: box.Point.X + box.Width*0.5,
|
||||
Y: box.Point.Y + box.Height*0.5,
|
||||
}
|
||||
}
|
||||
|
||||
type UIResult struct {
|
||||
Box
|
||||
}
|
||||
|
||||
type UIResults []UIResult
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/code"
|
||||
)
|
||||
|
||||
@@ -188,7 +189,7 @@ func (dExt *DriverExt) swipeToTapApp(appName string, options ...ActionOption) er
|
||||
options = append(options, WithOffset(0, -25))
|
||||
}
|
||||
// set default swipe interval to 1 second
|
||||
if actionOptions.Interval == 0 {
|
||||
if builtin.IsZeroFloat64(actionOptions.Interval) {
|
||||
options = append(options, WithInterval(1))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user