diff --git a/internal/runner/runner.go b/internal/runner/runner.go index c71b379..128f835 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -73,6 +73,7 @@ func Run(ctx context.Context, options Options) (Summary, error) { var hierarchyErr error var metrics *trace.Metrics var logs []verifier.LogEntry + var htmlCaptured bool g, _ := errgroup.WithContext(ctx) g.Go(func() error { @@ -89,12 +90,24 @@ func Run(ctx context.Context, options Options) (Summary, error) { logs = collectLogs(ctx, options.Driver, logSince) return nil }) + if web, ok := options.Driver.(driver.WebDriver); ok { + g.Go(func() error { + htmlCaptured = captureHTML(ctx, web, options.TraceWriter, logger, si, false) + return nil + }) + } if pendingPostScreenshot { postStep := pendingPostScreenshotStep g.Go(func() error { captureScreenshot(ctx, options, logger, postStep, true) return nil }) + if web, ok := options.Driver.(driver.WebDriver); ok { + g.Go(func() error { + captureHTML(ctx, web, options.TraceWriter, logger, postStep, true) + return nil + }) + } pendingPostScreenshot = false } g.Wait() @@ -148,14 +161,15 @@ func Run(ctx context.Context, options Options) (Summary, error) { } step := trace.Step{ - Index: stepIndex, - Timestamp: stepStart, - Screen: screen, - Action: traceAction, - Violations: violations, - Hierarchy: tree, - Residuals: residuals, - Metrics: metrics, + Index: stepIndex, + Timestamp: stepStart, + Screen: screen, + Action: traceAction, + Violations: violations, + Hierarchy: tree, + Residuals: residuals, + Metrics: metrics, + HTMLAvailable: htmlCaptured, } if err := options.TraceWriter.WriteStep(step); err != nil { return summary, fmt.Errorf("step %d trace: %w", stepIndex, err) @@ -196,6 +210,9 @@ func Run(ctx context.Context, options Options) (Summary, error) { if pendingPostScreenshot { captureScreenshot(ctx, options, logger, pendingPostScreenshotStep, true) + if web, ok := options.Driver.(driver.WebDriver); ok { + captureHTML(ctx, web, options.TraceWriter, logger, pendingPostScreenshotStep, true) + } } summary.EndTime = time.Now() @@ -397,6 +414,32 @@ func captureMetrics(ctx context.Context, options Options, logger *slog.Logger, s } } +// captureHTML pulls the current document HTML from a WebDriver-capable +// driver and stamps it into the trace under html/. Returns true on a +// successful non-empty write so the Step.HTMLAvailable flag advertises the +// payload to the inspect UI. +func captureHTML(ctx context.Context, web driver.WebDriver, writer *trace.Writer, logger *slog.Logger, stepIndex int, after bool) bool { + html, err := web.Document(ctx) + if err != nil { + logger.Warn("html capture failed", "step", stepIndex, "after", after, "err", err) + return false + } + if html == "" { + return false + } + var writeErr error + if after { + writeErr = writer.WriteHTMLAfter(stepIndex, []byte(html)) + } else { + writeErr = writer.WriteHTML(stepIndex, []byte(html)) + } + if writeErr != nil { + logger.Warn("html write failed", "step", stepIndex, "after", after, "err", writeErr) + return false + } + return true +} + func captureScreenshot(ctx context.Context, options Options, logger *slog.Logger, stepIndex int, after bool) { image, err := options.Driver.Screenshot(ctx) if err != nil {