diff --git a/examples/uitest/harmony_e2e_delay_test.go b/examples/uitest/harmony_e2e_delay_test.go index 7ec4d106..9f4a6dc4 100644 --- a/examples/uitest/harmony_e2e_delay_test.go +++ b/examples/uitest/harmony_e2e_delay_test.go @@ -56,4 +56,9 @@ func TestHarmonyDouyinE2E(t *testing.T) { if err := testCase.Dump2JSON("harmony_e2e_delay_test.json"); err != nil { t.Fatal(err) } + + err := hrp.Run(t, testCase) + if err != nil { + t.Fatal(err) + } } diff --git a/examples/uitest/harmony_e2e_delay_test.json b/examples/uitest/harmony_e2e_delay_test.json new file mode 100644 index 00000000..dec1492e --- /dev/null +++ b/examples/uitest/harmony_e2e_delay_test.json @@ -0,0 +1,149 @@ +{ + "config": { + "name": "直播_抖音_端到端时延_harmony", + "variables": { + "device": "${ENV(SerialNumber)}", + "ups": "${ENV(LIVEUPLIST)}" + }, + "harmony": [ + { + "connect_key": "$device", + "log_on": true + } + ] + }, + "teststeps": [ + { + "name": "启动抖音", + "harmony": { + "actions": [ + { + "method": "app_terminate", + "params": "com.ss.hm.ugc.aweme" + }, + { + "method": "swipe_to_tap_app", + "params": "com.ss.hm.ugc.aweme", + "options": { + + } + }, + { + "method": "home" + }, + { + "method": "swipe_to_tap_app", + "params": "抖音", + "options": { + "max_retry_times": 5, + "offset": [ + 0, + -50 + ] + } + }, + { + "method": "sleep", + "params": 20 + } + ] + }, + "validate": [ + { + "check": "ui_ocr", + "assert": "exists", + "expect": "推荐", + "msg": "进入抖音失败" + } + ] + }, + { + "name": "点击放大镜", + "harmony": { + "actions": [ + { + "method": "tap_xy", + "params": [ + 0.9, + 0.08 + ], + "options": { + + } + }, + { + "method": "sleep", + "params": 5 + } + ] + } + }, + { + "name": "输入账号名称", + "harmony": { + "actions": [ + { + "method": "input", + "params": "$ups", + "options": { + + } + }, + { + "method": "sleep", + "params": 5 + } + ] + } + }, + { + "name": "点击搜索", + "harmony": { + "actions": [ + { + "method": "tap_ocr", + "params": "搜索", + "options": { + + } + }, + { + "method": "sleep", + "params": 5 + } + ] + } + }, + { + "name": "端到端采集", + "harmony": { + "actions": [ + { + "method": "tap_ocr", + "params": "直播中", + "options": { + "ignore_NotFoundError": true, + "index": -1 + } + }, + { + "method": "live_e2e", + "options": { + "interval": 5, + "timeout": 120 + } + }, + { + "method": "tap_cv", + "options": { + "screenshot_with_ui_types": [ + "close" + ] + } + } + ] + }, + "loops": 5 + } + ] +} diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index dc204247..d8ddf515 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v5.0.0-beta-2409251549 \ No newline at end of file +v5.0.0-beta-2409252159 diff --git a/hrp/runner.go b/hrp/runner.go index 1fa3104f..27562aad 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "os/signal" + "reflect" "strings" "syscall" "testing" @@ -404,9 +405,63 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) { } r.parametersIterator = parametersIterator + // parse android/ios/harmony config + for _, device := range parsedConfig.Android { + err := r.parseDeviceConfig(device, parsedConfig.Variables) + if err != nil { + return nil, errors.Wrap(code.InvalidCaseError, + fmt.Sprintf("parse android config failed: %v", err)) + } + } + for _, device := range parsedConfig.IOS { + err := r.parseDeviceConfig(device, parsedConfig.Variables) + if err != nil { + return nil, errors.Wrap(code.InvalidCaseError, + fmt.Sprintf("parse ios config failed: %v", err)) + } + } + for _, device := range parsedConfig.Harmony { + err := r.parseDeviceConfig(device, parsedConfig.Variables) + if err != nil { + return nil, errors.Wrap(code.InvalidCaseError, + fmt.Sprintf("parse harmony config failed: %v", err)) + } + } return parsedConfig, nil } +func (r *CaseRunner) parseDeviceConfig( + device interface{}, configVariables map[string]interface{}, +) error { + deviceValue := reflect.ValueOf(device).Elem() + deviceType := deviceValue.Type() + for i := 0; i < deviceType.NumField(); i++ { + field := deviceType.Field(i) + fieldValue := deviceValue.Field(i) + if fieldValue.Kind() != reflect.String { + continue + } + + parsedValue, err := r.parser.ParseString( + fieldValue.String(), configVariables) + if err != nil { + log.Error().Err(err).Msgf( + "parse config device variable %s failed", field.Name) + return err + } + + parsedValueReflect := reflect.ValueOf(parsedValue) + if parsedValueReflect.Type().ConvertibleTo(fieldValue.Type()) { + convertedValue := parsedValueReflect.Convert(fieldValue.Type()) + fieldValue.Set(convertedValue) + } else { + log.Error().Msgf("update config device variable %s failed", field.Name) + return err + } + } + return nil +} + // each boomer task initiates a new session // in order to avoid data racing func (r *CaseRunner) NewSession() *SessionRunner { diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 1930b71c..40051572 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -73,6 +73,8 @@ type StepMobile struct { func (s *StepMobile) obj() *MobileUI { if s.step.IOS != nil { return s.step.IOS + } else if s.step.Harmony != nil { + return s.step.Harmony } return s.step.Android } @@ -593,13 +595,26 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err // ios step osType = "ios" mobileStep = step.IOS - } else if step.Android != nil { + iosDevices := s.caseRunner.Config.IOS + if mobileStep.Serial == "" && len(iosDevices) > 0 { + mobileStep.Serial = iosDevices[0].UDID + } + } else if step.Harmony != nil { + // harmony step + osType = "harmony" + mobileStep = step.Harmony + harmonyDevices := s.caseRunner.Config.Harmony + if mobileStep.Serial == "" && len(harmonyDevices) > 0 { + mobileStep.Serial = harmonyDevices[0].ConnectKey + } + } else { // android step osType = "android" mobileStep = step.Android - } else { - osType = "harmony" - mobileStep = step.Harmony + androidDevices := s.caseRunner.Config.Android + if mobileStep.Serial == "" && len(androidDevices) > 0 { + mobileStep.Serial = androidDevices[0].SerialNumber + } } // report GA event