diff --git a/internal/version/VERSION b/internal/version/VERSION index 6926ae89..756610de 100644 --- a/internal/version/VERSION +++ b/internal/version/VERSION @@ -1 +1 @@ -v5.0.0-beta-2506182235 +v5.0.0-beta-2506182314 diff --git a/report.go b/report.go index 9cc202b2..75904a2e 100644 --- a/report.go +++ b/report.go @@ -2575,7 +2575,7 @@ const htmlTemplate = ` {{end}} - {{if eq $validator.check "ui_ai"}} + {{if or (eq $validator.check "ui_ai") (eq $validator.assert "ai_assert")}}
{{$stepLogs := getStepLogs $step}} diff --git a/step_ui.go b/step_ui.go index 1fec9f4b..da922435 100644 --- a/step_ui.go +++ b/step_ui.go @@ -852,10 +852,7 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err return stepResult, errors.Wrap(code.InterruptError, "mobile UI runner interrupted") default: actionStartTime := time.Now() - actionResult := &ActionResult{ - MobileAction: action, - StartTime: actionStartTime.UnixMilli(), // action 开始时间 - } + // Parse action params first for variable substitution if action.Params, err = s.caseRunner.parser.Parse(action.Params, stepVariables); err != nil { if !code.IsErrorPredefined(err) { err = errors.Wrap(code.ParseError, @@ -864,6 +861,12 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err return stepResult, err } + // Create ActionResult with parsed params for accurate reporting + actionResult := &ActionResult{ + MobileAction: action, // Now contains parsed params + StartTime: actionStartTime.UnixMilli(), // action start time + } + // Apply global configuration from testcase config if s.caseRunner != nil && s.caseRunner.Config != nil { config := s.caseRunner.Config.Get() @@ -961,55 +964,111 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err } // validate - validateResults, err := validateUI(uiDriver, stepValidators) - if err != nil { - if !code.IsErrorPredefined(err) { - err = errors.Wrap(code.MobileUIValidationError, err.Error()) - } - return - } + validateResults, err := validateUI(uiDriver, stepValidators, s.caseRunner.parser, stepVariables) if len(validateResults) > 0 { + // Always save validation results if any exist, regardless of success or failure sessionData := &SessionData{ Validators: validateResults, } stepResult.Data = sessionData } + if err != nil { + // Handle validation error after saving results + if !code.IsErrorPredefined(err) { + err = errors.Wrap(code.MobileUIValidationError, err.Error()) + } + return stepResult, err + } + stepResult.Success = true return stepResult, nil } -func validateUI(ud *uixt.XTDriver, iValidators []interface{}) (validateResults []*ValidationResult, err error) { +func validateUI(ud *uixt.XTDriver, iValidators []interface{}, parser *Parser, stepVariables map[string]interface{}) (validateResults []*ValidationResult, err error) { + // Parse all validators for variable substitution + parsedValidators, err := parseStepValidators(iValidators, parser, stepVariables) + if err != nil { + return nil, err + } + + // Execute validation for each parsed validator + for _, validator := range parsedValidators { + // Debug: print validator details + log.Debug(). + Str("check", validator.Check). + Str("assert", validator.Assert). + Interface("expect", validator.Expect). + Str("message", validator.Message). + Msg("processing validator") + + validationResult := &ValidationResult{ + Validator: validator, // Use parsed validator for accurate reporting + CheckResult: "fail", + } + + // Check if this is a UI validator or AI assert validator + if !strings.HasPrefix(validator.Check, "ui_") && validator.Assert != "ai_assert" { + validationResult.CheckResult = "skip" + log.Warn().Interface("validator", validator).Msg("skip validator") + validateResults = append(validateResults, validationResult) + continue + } + + // Validate expected value type + expected, ok := validator.Expect.(string) + if !ok { + return nil, errors.New("validator expect should be string") + } + + // Perform validation + err = ud.DoValidation(validator.Check, validator.Assert, expected, validator.Message) + if err != nil { + // Add the failed validation result to the list before returning error + validateResults = append(validateResults, validationResult) + return validateResults, errors.Wrap(err, "step validation failed") + } + + validationResult.CheckResult = "pass" + validateResults = append(validateResults, validationResult) + } + + return validateResults, nil +} + +// parseStepValidators parses all validators for variable substitution +func parseStepValidators(iValidators []interface{}, parser *Parser, stepVariables map[string]interface{}) ([]Validator, error) { + var parsedValidators []Validator + for _, iValidator := range iValidators { validator, ok := iValidator.(Validator) if !ok { return nil, errors.New("validator type error") } - validataResult := &ValidationResult{ - Validator: validator, - CheckResult: "fail", + parsedValidator := validator + + // Parse Expect field for variable substitution + if expectedStr, ok := validator.Expect.(string); ok { + if parsedExpected, err := parser.Parse(expectedStr, stepVariables); err != nil { + return nil, errors.Wrap(err, "failed to parse validator expect field") + } else { + parsedValidator.Expect = parsedExpected + } } - // parse check value - if !strings.HasPrefix(validator.Check, "ui_") { - validataResult.CheckResult = "skip" - log.Warn().Interface("validator", validator).Msg("skip validator") - validateResults = append(validateResults, validataResult) - continue + // Parse Message field for variable substitution + if validator.Message != "" { + if parsedMessage, err := parser.Parse(validator.Message, stepVariables); err != nil { + return nil, errors.Wrap(err, "failed to parse validator message field") + } else { + if msgStr, ok := parsedMessage.(string); ok { + parsedValidator.Message = msgStr + } + } } - expected, ok := validator.Expect.(string) - if !ok { - return nil, errors.New("validator expect should be string") - } - - err := ud.DoValidation(validator.Check, validator.Assert, expected, validator.Message) - if err != nil { - return validateResults, errors.Wrap(err, "step validation failed") - } - - validataResult.CheckResult = "pass" - validateResults = append(validateResults, validataResult) + parsedValidators = append(parsedValidators, parsedValidator) } - return validateResults, nil + + return parsedValidators, nil }