diff --git a/hrp/pkg/uixt/action.go b/hrp/pkg/uixt/action.go index 242b59ea..cd3d7c4a 100644 --- a/hrp/pkg/uixt/action.go +++ b/hrp/pkg/uixt/action.go @@ -668,7 +668,19 @@ func (dExt *DriverExt) DoAction(action MobileAction) (err error) { } return dExt.VideoCrawler(configs) case ACTION_ClosePopups: - return dExt.ClosePopups(action.GetOptions()...) + options := action.GetOptions() + actionOptions := NewActionOptions(options...) + + // default to retry 3 times + if actionOptions.MaxRetryTimes == 0 { + options = append(options, WithMaxRetryTimes(3)) + } + // set default swipe interval to 1 second + if builtin.IsZeroFloat64(actionOptions.Interval) { + options = append(options, WithInterval(1)) + } + + return dExt.ClosePopupsHandler(options...) } return nil } diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index e292d93c..71d8e177 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -7,8 +7,6 @@ import ( "time" "github.com/httprunner/funplugin" - - "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) var ( @@ -438,8 +436,8 @@ type PointF struct { } func (p PointF) IsIdentical(p2 PointF) bool { - return builtin.IsZeroFloat64(math.Abs(p.X-p2.X)) && - builtin.IsZeroFloat64(math.Abs(p.Y-p2.Y)) + // set the coordinate precision to 1 pixel + return math.Abs(p.X-p2.X) < 1 && math.Abs(p.Y-p2.Y) < 1 } type Rect struct { diff --git a/hrp/pkg/uixt/popups.go b/hrp/pkg/uixt/popups.go index a1d01e07..2fd032d8 100644 --- a/hrp/pkg/uixt/popups.go +++ b/hrp/pkg/uixt/popups.go @@ -6,7 +6,6 @@ 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" ) @@ -89,22 +88,20 @@ type ClosePopupsResult struct { } type PopupInfo struct { - CloseStatus string `json:"close_status"` // found/success/fail - Type string `json:"type"` - Text string `json:"text"` - RetryCount int `json:"retry_count"` - PicName string `json:"pic_name"` - PicURL string `json:"pic_url"` - PopupArea Box `json:"popup_area"` - CloseArea Box `json:"close_area"` + CloseStatus string `json:"close_status"` // found/success/fail + RetryCount int `json:"retry_count"` + CloseBox Box `json:"close_box"` // CV 识别的弹窗关闭按钮(弹窗存在 && 关闭按钮存在) + ClosePoints []PointF `json:"close_points,omitempty"` // CV 识别的所有关闭按钮(仅关闭按钮,可能存在多个) } func (p *PopupInfo) isIdentical(lastPopup *PopupInfo) bool { - if lastPopup == nil || lastPopup.PopupArea.IsEmpty() { + if lastPopup == nil { return false } - - if !p.CloseArea.IsIdentical(lastPopup.CloseArea) { + if lastPopup.CloseBox.IsEmpty() { + return false + } + if !p.CloseBox.IsIdentical(lastPopup.CloseBox) { lastPopup.CloseStatus = CloseStatusSuccess return false } @@ -114,24 +111,6 @@ func (p *PopupInfo) isIdentical(lastPopup *PopupInfo) bool { return true } -func (p *PopupInfo) exists() bool { - return p.PopupArea.IsEmpty() || p.CloseArea.IsEmpty() -} - -func (dExt *DriverExt) ClosePopups(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.ClosePopupsHandler(options...) -} - func (dExt *DriverExt) ClosePopupsHandler(options ...ActionOption) error { actionOptions := NewActionOptions(options...) log.Info().Interface("actionOptions", actionOptions).Msg("try to find and close popups") @@ -141,15 +120,18 @@ func (dExt *DriverExt) ClosePopupsHandler(options ...ActionOption) error { var lastPopup *PopupInfo for retryCount := 0; retryCount < maxRetryTimes; retryCount++ { screenResult, err := dExt.GetScreenResult( - WithScreenShotClosePopups(true), WithScreenShotUpload(true)) + WithScreenShotUpload(true), + WithScreenShotClosePopups(true), + WithScreenShotUITypes("close"), // get all close buttons + ) if err != nil { log.Error().Err(err).Msg("get screen result failed for popup handler") continue } popup := screenResult.Popup - if popup == nil || !popup.exists() { - log.Debug().Msg("no popup found") + if popup == nil || popup.CloseBox.IsEmpty() { + log.Debug().Interface("popup", popup).Msg("no popup found") break } popup.CloseStatus = CloseStatusFound @@ -172,13 +154,13 @@ func (dExt *DriverExt) ClosePopupsHandler(options ...ActionOption) error { } func (dExt *DriverExt) tapPopupHandler(popup *PopupInfo) error { - if popup == nil || !popup.exists() { - log.Debug().Msg("no popup found") + if popup == nil || popup.CloseBox.IsEmpty() { + log.Debug().Interface("popup", popup).Msg("no popup found") return nil } popup.CloseStatus = CloseStatusFound - popupClose := popup.CloseArea + popupClose := popup.CloseBox if popupClose.IsEmpty() { log.Error(). Interface("popup", popup). diff --git a/hrp/pkg/uixt/service_vedem.go b/hrp/pkg/uixt/service_vedem.go index 7723a731..1660431e 100644 --- a/hrp/pkg/uixt/service_vedem.go +++ b/hrp/pkg/uixt/service_vedem.go @@ -67,9 +67,9 @@ type ImageResult struct { // Media(媒体) // Chat(语音) // Event(赛事) - LiveType string `json:"liveType,omitempty"` // 直播间类型 - UIResult UIResultMap `json:"uiResult,omitempty"` // 图标检测 - CPResult *ClosePopupsResult `json:"closeResult,omitempty"` // 弹窗按钮检测 + LiveType string `json:"liveType,omitempty"` // 直播间类型 + UIResult UIResultMap `json:"uiResult,omitempty"` // 图标检测 + ClosePopupsResult *ClosePopupsResult `json:"closeResult,omitempty"` // 弹窗按钮检测 } type APIResponseImage struct { @@ -407,15 +407,20 @@ func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *S screenResult.UploadedURL = imageResult.URL screenResult.Icons = imageResult.UIResult - if actionOptions.ScreenShotWithClosePopups && imageResult.CPResult != nil { - screenResult.Popup = &PopupInfo{ - Type: imageResult.CPResult.Type, - Text: imageResult.CPResult.Text, - PicName: imagePath, - PicURL: imageResult.URL, - PopupArea: imageResult.CPResult.PopupArea, - CloseArea: imageResult.CPResult.CloseArea, + if actionOptions.ScreenShotWithClosePopups { + popup := &PopupInfo{} + + closeResult := imageResult.ClosePopupsResult + if closeResult != nil && !closeResult.PopupArea.IsEmpty() && !closeResult.CloseArea.IsEmpty() { + popup.CloseBox = closeResult.CloseArea } + + closeAreas, _ := imageResult.UIResult.FilterUIResults([]string{"close"}) + for _, closeArea := range closeAreas { + popup.ClosePoints = append(popup.ClosePoints, closeArea.Center()) + } + + screenResult.Popup = popup } } @@ -478,9 +483,10 @@ func (box Box) IsEmpty() bool { } func (box Box) IsIdentical(box2 Box) bool { + // set the coordinate precision to 1 pixel return box.Point.IsIdentical(box2.Point) && - builtin.IsZeroFloat64(math.Abs(box.Width-box2.Width)) && - builtin.IsZeroFloat64(math.Abs(box.Height-box2.Height)) + math.Abs(box.Width-box2.Width) < 1 && + math.Abs(box.Height-box2.Height) < 1 } func (box Box) Center() PointF { diff --git a/hrp/pkg/uixt/video_crawler.go b/hrp/pkg/uixt/video_crawler.go index faf31c76..1f6a2905 100644 --- a/hrp/pkg/uixt/video_crawler.go +++ b/hrp/pkg/uixt/video_crawler.go @@ -186,7 +186,7 @@ func (dExt *DriverExt) VideoCrawler(configs *VideoCrawlerConfigs) (err error) { log.Warn().Msg("get current feed video failed") // check and handle popups - if err := crawler.driverExt.ClosePopupsHandler(WithMaxRetryTimes(3)); err != nil { + if err := crawler.driverExt.ClosePopupsHandler(WithMaxRetryTimes(1)); err != nil { return err } diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 66014878..85b8dfa4 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -624,7 +624,7 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err } // automatic handling of pop-up windows on each step finished - if err2 := uiDriver.ClosePopups(); err2 != nil { + if err2 := uiDriver.ClosePopupsHandler(uixt.WithMaxRetryTimes(3), uixt.WithInterval(1)); err2 != nil { log.Error().Err(err2).Str("step", step.Name).Msg("handle popup failed on step finished") }