feat: add comprehensive HTML report generation with log filtering

- Add complete HTML report generator with template-based rendering
- Implement log time filtering for step-specific logs
- Support responsive design and interactive UI features
- Consolidate duplicate report implementations
This commit is contained in:
lilong.129
2025-06-08 09:23:14 +08:00
parent ec4f1eb68a
commit 4053cc9985
5 changed files with 1283 additions and 26 deletions
+1
View File
@@ -17,6 +17,7 @@ func addAllCommands() {
cmd.RootCmd.AddCommand(cmd.CmdBuild) cmd.RootCmd.AddCommand(cmd.CmdBuild)
cmd.RootCmd.AddCommand(cmd.CmdConvert) cmd.RootCmd.AddCommand(cmd.CmdConvert)
cmd.RootCmd.AddCommand(cmd.CmdPytest) cmd.RootCmd.AddCommand(cmd.CmdPytest)
cmd.RootCmd.AddCommand(cmd.CmdReport)
cmd.RootCmd.AddCommand(cmd.CmdRun) cmd.RootCmd.AddCommand(cmd.CmdRun)
cmd.RootCmd.AddCommand(cmd.CmdScaffold) cmd.RootCmd.AddCommand(cmd.CmdScaffold)
cmd.RootCmd.AddCommand(cmd.CmdServer) cmd.RootCmd.AddCommand(cmd.CmdServer)
+39
View File
@@ -0,0 +1,39 @@
package cmd
import (
"fmt"
"path/filepath"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
hrp "github.com/httprunner/httprunner/v5"
)
var CmdReport = &cobra.Command{
Use: "report [result_folder]",
Short: "Generate HTML report from test results",
Long: `Generate report.html from test results in the specified folder.
The folder should contain summary.json and optionally hrp.log files.
Examples:
$ hrp report results/20250607234602/
$ hrp report /path/to/test/results/`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
resultFolder := args[0]
// Construct file paths
summaryFile := filepath.Join(resultFolder, "summary.json")
logFile := filepath.Join(resultFolder, "hrp.log")
reportFile := filepath.Join(resultFolder, "report.html")
// Generate HTML report
if err := hrp.GenerateHTMLReportFromFiles(summaryFile, logFile, reportFile); err != nil {
return fmt.Errorf("failed to generate HTML report: %w", err)
}
log.Info().Str("report_file", reportFile).Msg("HTML report generated successfully")
return nil
},
}
+1 -1
View File
@@ -1 +1 @@
v5.0.0-beta-2506072359 v5.0.0-beta-2506080923
+1231
View File
File diff suppressed because it is too large Load Diff
+10 -24
View File
@@ -1,11 +1,8 @@
package hrp package hrp
import ( import (
"bufio"
_ "embed" _ "embed"
"fmt" "fmt"
"html/template"
"os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"time" "time"
@@ -94,27 +91,19 @@ func (s *Summary) GenHTMLReport() error {
return err return err
} }
// Find summary.json and hrp.log files
summaryPath := filepath.Join(reportsDir, "summary.json")
logPath := filepath.Join(reportsDir, "hrp.log")
reportPath := filepath.Join(reportsDir, "report.html") reportPath := filepath.Join(reportsDir, "report.html")
file, err := os.Open(reportPath)
if err != nil { // Check if summary.json exists, if not create it first
log.Error().Err(err).Msg("open file failed") if !builtin.FileExists(summaryPath) {
return err if _, err := s.GenSummary(); err != nil {
return fmt.Errorf("failed to generate summary.json: %w", err)
} }
defer file.Close()
writer := bufio.NewWriter(file)
tmpl := template.Must(template.New("report").Parse(reportTemplate))
err = tmpl.Execute(writer, s)
if err != nil {
log.Error().Err(err).Msg("execute applies a parsed template to the specified data object failed")
return err
} }
err = writer.Flush()
if err == nil { return GenerateHTMLReportFromFiles(summaryPath, logPath, reportPath)
log.Info().Str("path", reportPath).Msg("generate HTML report")
} else {
log.Error().Str("path", reportPath).Msg("generate HTML report failed")
}
return err
} }
func (s *Summary) GenSummary() (path string, err error) { func (s *Summary) GenSummary() (path string, err error) {
@@ -131,9 +120,6 @@ func (s *Summary) GenSummary() (path string, err error) {
return path, nil return path, nil
} }
//go:embed internal/scaffold/templates/report/template.html
var reportTemplate string
type Stat struct { type Stat struct {
TestCases TestCaseStat `json:"testcases" yaml:"testcases"` TestCases TestCaseStat `json:"testcases" yaml:"testcases"`
TestSteps TestStepStat `json:"teststeps" yaml:"teststeps"` TestSteps TestStepStat `json:"teststeps" yaml:"teststeps"`