From c611a84aaafad87a4755354138c3aa8e68e74bba Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Sun, 27 Aug 2023 12:09:22 +0800 Subject: [PATCH] optimize hrp summary popup info --- examples/uitest/android_expert_test.json | 17 +++---- examples/uitest/expert_test.go | 32 +++++------- examples/uitest/ios_expert_test.json | 17 +++---- hrp/pkg/uixt/action.go | 22 ++++----- hrp/pkg/uixt/ext.go | 63 ++++++++++++++++++++---- hrp/pkg/uixt/interface.go | 6 --- hrp/pkg/uixt/popups.go | 26 ++++++---- hrp/pkg/uixt/service_vedem.go | 16 ++++-- hrp/step_mobile_ui.go | 9 ++++ 9 files changed, 129 insertions(+), 79 deletions(-) diff --git a/examples/uitest/android_expert_test.json b/examples/uitest/android_expert_test.json index 97edc084..8930c309 100644 --- a/examples/uitest/android_expert_test.json +++ b/examples/uitest/android_expert_test.json @@ -59,15 +59,12 @@ } }, { - "name": "处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言", + "name": "处理弹窗 close_popups 默认配置 以及 ui_ocr exists 断言", "android": { "actions": [ { - "method": "tap_ocr", - "params": "我知道了", - "options": { - "ignore_NotFoundError": true - } + "method": "close_popups", + "options": {} } ] }, @@ -317,14 +314,14 @@ } }, { - "name": "处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言", + "name": "处理弹窗 close_popups 自定义配置 以及 ui_ocr exists 断言", "android": { "actions": [ { - "method": "tap_ocr", - "params": "我知道了", + "method": "close_popups", "options": { - "ignore_NotFoundError": true + "max_retry_times": 3, + "interval": 2 } } ] diff --git a/examples/uitest/expert_test.go b/examples/uitest/expert_test.go index 0067f625..07871184 100644 --- a/examples/uitest/expert_test.go +++ b/examples/uitest/expert_test.go @@ -1,5 +1,3 @@ -//go:build localtest - package uitest import ( @@ -36,12 +34,9 @@ func TestAndroidExpertTest(t *testing.T) { Home(). SwipeToTapApp("$app_name"). Sleep(10), - hrp.NewStep("处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言"). + hrp.NewStep("处理弹窗 close_popups 默认配置 以及 ui_ocr exists 断言"). Android(). - TapByOCR( - "我知道了", - uixt.WithIgnoreNotFoundError(true), - ). + ClosePopups(). Validate(). AssertOCRExists("推荐", "进入抖音失败"), // 直播赛道 @@ -118,11 +113,11 @@ func TestAndroidExpertTest(t *testing.T) { Home(). SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithInterval(1), uixt.WithOffset(0, -50)). Sleep(10), - hrp.NewStep("处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言"). + hrp.NewStep("处理弹窗 close_popups 自定义配置 以及 ui_ocr exists 断言"). Android(). - TapByOCR( - "我知道了", - uixt.WithIgnoreNotFoundError(true), + ClosePopups( + uixt.WithMaxRetryTimes(3), + uixt.WithInterval(2), ). Validate(). AssertOCRExists("推荐", "进入抖音失败"), @@ -194,12 +189,9 @@ func TestIOSExpertTest(t *testing.T) { Home(). SwipeToTapApp("$app_name"). Sleep(10), - hrp.NewStep("处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言"). + hrp.NewStep("处理弹窗 close_popups 默认配置 以及 ui_ocr exists 断言"). IOS(). - TapByOCR( - "我知道了", - uixt.WithIgnoreNotFoundError(true), - ). + ClosePopups(). Validate(). AssertOCRExists("推荐", "进入抖音失败"), // 直播赛道 @@ -275,11 +267,11 @@ func TestIOSExpertTest(t *testing.T) { Home(). SwipeToTapApp("$app_name", uixt.WithMaxRetryTimes(5), uixt.WithInterval(1), uixt.WithOffset(0, -50)). Sleep(10), - hrp.NewStep("处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言"). + hrp.NewStep("处理弹窗 close_popups 自定义配置 以及 ui_ocr exists 断言"). IOS(). - TapByOCR( - "我知道了", - uixt.WithIgnoreNotFoundError(true), + ClosePopups( + uixt.WithMaxRetryTimes(3), + uixt.WithInterval(2), ). Validate(). AssertOCRExists("推荐", "进入抖音失败"), diff --git a/examples/uitest/ios_expert_test.json b/examples/uitest/ios_expert_test.json index 85fccf35..3f29bb00 100644 --- a/examples/uitest/ios_expert_test.json +++ b/examples/uitest/ios_expert_test.json @@ -52,15 +52,12 @@ } }, { - "name": "处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言", + "name": "处理弹窗 close_popups 默认配置 以及 ui_ocr exists 断言", "ios": { "actions": [ { - "method": "tap_ocr", - "params": "我知道了", - "options": { - "ignore_NotFoundError": true - } + "method": "close_popups", + "options": {} } ] }, @@ -302,14 +299,14 @@ } }, { - "name": "处理青少年弹窗 tap ocr 以及 ui_ocr exists 断言", + "name": "处理弹窗 close_popups 自定义配置 以及 ui_ocr exists 断言", "ios": { "actions": [ { - "method": "tap_ocr", - "params": "我知道了", + "method": "close_popups", "options": { - "ignore_NotFoundError": true + "max_retry_times": 3, + "interval": 2 } } ] diff --git a/hrp/pkg/uixt/action.go b/hrp/pkg/uixt/action.go index 5f803fe7..fdb9dbdf 100644 --- a/hrp/pkg/uixt/action.go +++ b/hrp/pkg/uixt/action.go @@ -115,11 +115,11 @@ type ActionOptions struct { Custom map[string]interface{} `json:"custom,omitempty" yaml:"custom,omitempty"` // screenshot related - ScreenShotWithOCR bool `json:"screenshot_with_ocr,omitempty" yaml:"screenshot_with_ocr,omitempty"` - 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"` + ScreenShotWithOCR bool `json:"screenshot_with_ocr,omitempty" yaml:"screenshot_with_ocr,omitempty"` + 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"` + ScreenShotWithClosePopups bool `json:"screenshot_with_close_popups,omitempty" yaml:"screenshot_with_close_popups,omitempty"` } func (o *ActionOptions) Options() []ActionOption { @@ -219,12 +219,12 @@ func (o *ActionOptions) Options() []ActionOption { func (o *ActionOptions) screenshotActions() []string { actions := []string{} - if o.ScreenShotWithOCR { - actions = append(actions, "ocr") - } if o.ScreenShotWithUpload { actions = append(actions, "upload") } + if o.ScreenShotWithOCR { + actions = append(actions, "ocr") + } if o.ScreenShotWithLiveType { actions = append(actions, "liveType") } @@ -232,7 +232,7 @@ func (o *ActionOptions) screenshotActions() []string { if len(o.ScreenShotWithUITypes) > 0 { actions = append(actions, "ui") } - if o.ScreenShotWithClose { + if o.ScreenShotWithClosePopups { actions = append(actions, "close") } return actions @@ -443,9 +443,9 @@ func WithScreenShotUITypes(uiTypes ...string) ActionOption { } } -func WithScreenShotClose(closeOn bool) ActionOption { +func WithScreenShotClosePopups(closeOn bool) ActionOption { return func(o *ActionOptions) { - o.ScreenShotWithClose = closeOn + o.ScreenShotWithClosePopups = closeOn } } diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 9c946df0..b34db8bd 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -15,6 +15,7 @@ import ( "os" "os/signal" "path/filepath" + "sort" "strings" "syscall" "time" @@ -61,7 +62,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"` + Popup *PopupInfo `json:"popup,omitempty"` SwipeStartTime int64 `json:"swipe_start_time"` // 滑动开始时间戳 SwipeFinishTime int64 `json:"swipe_finish_time"` // 滑动结束时间戳 @@ -73,11 +74,58 @@ type ScreenResult struct { TotalElapsed int64 `json:"total_elapsed"` // current_swipe_finish -> next_swipe_start 整体耗时(ms) } +type ScreenResultMap map[string]*ScreenResult + +// getScreenShotUrls returns screenShotsUrls using imagePath as key and uploaded URL as value +func (screenResults ScreenResultMap) getScreenShotUrls() map[string]string { + screenShotsUrls := make(map[string]string) + for imagePath, screenResult := range screenResults { + if screenResult.UploadedURL == "" { + continue + } + screenShotsUrls[imagePath] = screenResult.UploadedURL + } + return screenShotsUrls +} + +// checkPopupInfo checks if popup closed normally in every screenResult with close_popups on: +func (screenResults ScreenResultMap) checkPopupInfo() { + var screenResultList []*ScreenResult + for _, screenResult := range screenResults { + if screenResult.Popup == nil { + continue + } + screenResultList = append(screenResultList, screenResult) + } + if len(screenResultList) == 0 { + return + } + sort.Slice(screenResultList, func(i, j int) bool { + return screenResultList[i].Popup.RetryCount < screenResultList[j].Popup.RetryCount + }) + + for i := 0; i < len(screenResultList)-1; i++ { + curPopup := screenResultList[i].Popup + nextPopup := screenResultList[i+1].Popup + + // popup not existed, no need to close + if curPopup.CloseArea.IsEmpty() { + continue + } + // popup existed, but identical popups occurs during next retry + if curPopup.Text == nextPopup.Text && curPopup.Type == nextPopup.Type { + continue + } + // popup existed, but no popup or different popup occurs during next retry (IsClosed=true) + screenResultList[i].Popup.IsClosed = true + } +} + type cacheStepData struct { // cache step screenshot paths screenShots []string // cache step screenshot ocr results, key is image path, value is ScreenResult - screenResults map[string]*ScreenResult + screenResults ScreenResultMap // cache feed/live video stat videoCrawler *VideoCrawler } @@ -224,14 +272,8 @@ func (dExt *DriverExt) GetStepCacheData() map[string]interface{} { cacheData["video_stat"] = dExt.cacheStepData.videoCrawler cacheData["screenshots"] = dExt.cacheStepData.screenShots - screenShotsUrls := make(map[string]string) - for imagePath, screenResult := range dExt.cacheStepData.screenResults { - if screenResult.UploadedURL == "" { - continue - } - screenShotsUrls[imagePath] = screenResult.UploadedURL - } - cacheData["screenshots_urls"] = screenShotsUrls + cacheData["screenshots_urls"] = dExt.cacheStepData.screenResults.getScreenShotUrls() + dExt.cacheStepData.screenResults.checkPopupInfo() screenSize, err := dExt.Driver.WindowSize() if err != nil { @@ -256,6 +298,7 @@ func (dExt *DriverExt) GetStepCacheData() map[string]interface{} { "screenshot_take_elapsed": screenResult.ScreenshotTakeElapsed, "screenshot_cv_elapsed": screenResult.ScreenshotCVElapsed, "total_elapsed": screenResult.TotalElapsed, + "icons": screenResult.Icons, "popup": screenResult.Popup, } diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index 7eda0c2e..e816573d 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -6,8 +6,6 @@ import ( "time" "github.com/httprunner/funplugin" - - "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) var ( @@ -436,10 +434,6 @@ 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 diff --git a/hrp/pkg/uixt/popups.go b/hrp/pkg/uixt/popups.go index ecbebc22..c7ad3807 100644 --- a/hrp/pkg/uixt/popups.go +++ b/hrp/pkg/uixt/popups.go @@ -83,13 +83,14 @@ type CPResult struct { } type PopupInfo struct { - // TODO: support more types of popup report info - // Status bool `json:"status"` + IsClosed bool `json:"is_closed"` Type string `json:"type"` + Text string `json:"text"` RetryCount int `json:"retry_count"` PicName string `json:"pic_name"` PicURL string `json:"pic_url"` - Point PointF `json:"point"` + PopupArea Box `json:"popup_area"` + CloseArea Box `json:"close_area"` } func (dExt *DriverExt) ClosePopups(options ...ActionOption) error { @@ -111,9 +112,9 @@ func (dExt *DriverExt) ClosePopupsHandler(options ...ActionOption) error { maxRetryTimes := actionOptions.MaxRetryTimes interval := actionOptions.Interval - for retryCount := 1; retryCount <= maxRetryTimes; retryCount++ { + for retryCount := 0; retryCount < maxRetryTimes; retryCount++ { screenResult, err := dExt.GetScreenResult( - WithScreenShotClose(true), WithScreenShotUpload(true)) + WithScreenShotClosePopups(true), WithScreenShotUpload(true)) if err != nil { log.Error().Err(err).Msg("get screen result failed for popup handler") continue @@ -124,6 +125,10 @@ func (dExt *DriverExt) ClosePopupsHandler(options ...ActionOption) error { if screenResult.Popup == nil { break } + screenResult.Popup.RetryCount = retryCount + if screenResult.Popup.CloseArea.IsEmpty() { + break + } if err = dExt.tapPopupHandler(screenResult.Popup); err != nil { return err @@ -134,12 +139,15 @@ func (dExt *DriverExt) ClosePopupsHandler(options ...ActionOption) error { return nil } -func (dExt *DriverExt) tapPopupHandler(cpResult *CPResult) error { - if cpResult == nil { +func (dExt *DriverExt) tapPopupHandler(popup *PopupInfo) error { + if popup == nil { return nil } - log.Info().Str("type", cpResult.Type).Str("text", cpResult.Text).Msg("close popup") - popupCenter := cpResult.CloseArea.Center() + if popup.CloseArea.IsEmpty() { + return nil + } + log.Info().Str("type", popup.Type).Str("text", popup.Text).Msg("close popup") + popupCenter := popup.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()) diff --git a/hrp/pkg/uixt/service_vedem.go b/hrp/pkg/uixt/service_vedem.go index 11ff2487..f2912fc8 100644 --- a/hrp/pkg/uixt/service_vedem.go +++ b/hrp/pkg/uixt/service_vedem.go @@ -8,6 +8,7 @@ import ( "mime/multipart" "net/http" "regexp" + "strings" "time" "github.com/pkg/errors" @@ -356,7 +357,8 @@ type IImageService interface { // GetScreenResult takes a screenshot, returns the image recognization result func (dExt *DriverExt) GetScreenResult(options ...ActionOption) (screenResult *ScreenResult, err error) { startTime := time.Now() - fileName := builtin.GenNameWithTimestamp("%d_screenshot") + actionOptions := NewActionOptions(options...) + fileName := builtin.GenNameWithTimestamp("%d_" + strings.Join(actionOptions.screenshotActions(), "_")) bufSource, imagePath, err := dExt.takeScreenShot(fileName) if err != nil { return @@ -386,9 +388,17 @@ 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 + if actionOptions.ScreenShotWithClosePopups { + screenResult.Popup = &PopupInfo{ + Type: imageResult.CPResult.Type, + Text: imageResult.CPResult.Text, + PicName: imagePath, + PicURL: imageResult.URL, + PopupArea: imageResult.CPResult.PopupArea, + CloseArea: imageResult.CPResult.CloseArea, + } } + } dExt.cacheStepData.screenResults[imagePath] = screenResult diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 47ea7850..50e22cbb 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -327,6 +327,15 @@ func (s *StepMobile) StopCamera() *StepMobile { return &StepMobile{step: s.step} } +func (s *StepMobile) ClosePopups(options ...uixt.ActionOption) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ + Method: uixt.ACTION_ClosePopups, + Params: nil, + Options: uixt.NewActionOptions(options...), + }) + return &StepMobile{step: s.step} +} + // Validate switches to step validation. func (s *StepMobile) Validate() *StepMobileUIValidation { return &StepMobileUIValidation{