mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-09-21 18:57:33 +03:00
Fix HTML report for retried requests.
This commit is contained in:
parent
2a06ab1287
commit
9f81b10d38
@ -36,16 +36,13 @@ impl Testcase {
|
||||
/// Creates an HTML testcase.
|
||||
pub fn from(hurl_result: &HurlResult, filename: &str) -> Testcase {
|
||||
let id = Uuid::new_v4();
|
||||
let errors = hurl_result.errors().into_iter().cloned().collect();
|
||||
Testcase {
|
||||
id: id.to_string(),
|
||||
filename: filename.to_string(),
|
||||
time_in_ms: hurl_result.time_in_ms,
|
||||
success: hurl_result.success,
|
||||
errors: hurl_result
|
||||
.entries
|
||||
.iter()
|
||||
.flat_map(|e| e.errors.clone())
|
||||
.collect(),
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,10 @@ use crate::report::html::timeline::svg::Attribute::{
|
||||
};
|
||||
use crate::report::html::timeline::svg::{Element, ElementKind};
|
||||
use crate::report::html::timeline::unit::{Pixel, Px};
|
||||
use crate::report::html::timeline::util::{new_failure_icon, new_success_icon, trunc_str};
|
||||
use crate::report::html::timeline::{svg, CallContext, CALL_HEIGHT};
|
||||
use crate::report::html::timeline::util::{
|
||||
new_failure_icon, new_retry_icon, new_success_icon, trunc_str,
|
||||
};
|
||||
use crate::report::html::timeline::{svg, CallContext, CallContextKind, CALL_HEIGHT};
|
||||
use crate::report::html::Testcase;
|
||||
use std::iter::zip;
|
||||
|
||||
@ -47,6 +49,8 @@ impl Testcase {
|
||||
root.add_child(symbol);
|
||||
let symbol = new_failure_icon("failure");
|
||||
root.add_child(symbol);
|
||||
let symbol = new_retry_icon("retry");
|
||||
root.add_child(symbol);
|
||||
|
||||
// Add a flat background.
|
||||
let mut elt = Element::new(ElementKind::Rect);
|
||||
@ -57,15 +61,17 @@ impl Testcase {
|
||||
elt.add_attr(Fill("#fbfafd".to_string()));
|
||||
root.add_child(elt);
|
||||
|
||||
// Add horizontal lines
|
||||
let x = 0.px();
|
||||
let y = margin_top;
|
||||
let elt = new_grid(calls, y, width, height);
|
||||
root.add_child(elt);
|
||||
if !calls.is_empty() {
|
||||
// Add horizontal lines
|
||||
let x = 0.px();
|
||||
let y = margin_top;
|
||||
let elt = new_grid(calls, y, width, height);
|
||||
root.add_child(elt);
|
||||
|
||||
// Add calls info
|
||||
let elt = new_calls(calls, call_ctxs, x, y);
|
||||
root.add_child(elt);
|
||||
// Add calls info
|
||||
let elt = new_calls(calls, call_ctxs, x, y);
|
||||
root.add_child(elt);
|
||||
}
|
||||
|
||||
root.to_string()
|
||||
}
|
||||
@ -96,10 +102,10 @@ fn new_calls(
|
||||
|
||||
// Icon success / failure
|
||||
let mut elt = svg::new_use();
|
||||
let icon = if call_ctx.success {
|
||||
"#success"
|
||||
} else {
|
||||
"#failure"
|
||||
let icon = match call_ctx.kind {
|
||||
CallContextKind::Success => "#success",
|
||||
CallContextKind::Failure => "#failure",
|
||||
CallContextKind::Retry => "#retry",
|
||||
};
|
||||
elt.add_attr(Href(icon.to_string()));
|
||||
elt.add_attr(X(x.0 - 6.0));
|
||||
@ -117,7 +123,7 @@ fn new_calls(
|
||||
let text = format!("{} {url}", call.request.method);
|
||||
let text = trunc_str(&text, 24);
|
||||
let mut elt = svg::new_text(x.0, y.0, &text);
|
||||
if !call_ctx.success {
|
||||
if call_ctx.kind == CallContextKind::Failure {
|
||||
elt.add_attr(Fill("red".to_string()));
|
||||
}
|
||||
group.add_child(elt);
|
||||
@ -126,7 +132,7 @@ fn new_calls(
|
||||
x += 180.px();
|
||||
let text = format!("{}", call.response.status);
|
||||
let mut elt = svg::new_text(x.0, y.0, &text);
|
||||
if !call_ctx.success {
|
||||
if call_ctx.kind == CallContextKind::Failure {
|
||||
elt.add_attr(Fill("red".to_string()));
|
||||
}
|
||||
group.add_child(elt);
|
||||
|
@ -33,14 +33,20 @@ mod waterfall;
|
||||
const CALL_HEIGHT: Pixel = Pixel(24.0);
|
||||
const CALL_INSET: Pixel = Pixel(3.0);
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum CallContextKind {
|
||||
Success, // call context parent entry is successful
|
||||
Failure, // call context parent entry is in error and has not been retried
|
||||
Retry, // call context parent entry is in error and has been retried
|
||||
}
|
||||
/// A structure that holds information to construct a SVG view
|
||||
/// of a [`Call`]
|
||||
pub struct CallContext {
|
||||
pub success: bool, // If the parent entry is successful or not
|
||||
pub line: usize, // Line number of the source entry (1-based)
|
||||
pub entry_index: usize, // Index of the runtime EntryResult
|
||||
pub kind: CallContextKind, // If the parent entry is successful, retried or in error.
|
||||
pub line: usize, // Line number of the source entry (1-based)
|
||||
pub entry_index: usize, // Index of the runtime EntryResult
|
||||
pub call_entry_index: usize, // Index of the runtime Call in the current entry
|
||||
pub call_index: usize, // Index of the runtime Call in the whole run
|
||||
pub call_index: usize, // Index of the runtime Call in the whole run
|
||||
pub source_filename: String,
|
||||
pub run_filename: String,
|
||||
}
|
||||
@ -80,13 +86,24 @@ impl Testcase {
|
||||
/// Constructs a list of call contexts to record source line code, runtime entry and call indices.
|
||||
fn get_call_contexts(&self, hurl_file: &HurlFile, entries: &[EntryResult]) -> Vec<CallContext> {
|
||||
let mut calls_ctx = vec![];
|
||||
|
||||
for (entry_index, e) in entries.iter().enumerate() {
|
||||
let next_e = entries.get(entry_index + 1);
|
||||
let retry = match next_e {
|
||||
None => false, // last entry of the whole run can't be retried
|
||||
Some(next_e) => e.entry_index == next_e.entry_index,
|
||||
};
|
||||
let kind = match (e.errors.is_empty(), retry) {
|
||||
(true, _) => CallContextKind::Success,
|
||||
(false, true) => CallContextKind::Retry,
|
||||
(false, false) => CallContextKind::Failure,
|
||||
};
|
||||
for (call_entry_index, _) in e.calls.iter().enumerate() {
|
||||
let entry_src_index = e.entry_index - 1;
|
||||
let entry_src = hurl_file.entries.get(entry_src_index).unwrap();
|
||||
let line = entry_src.request.space0.source_info.start.line;
|
||||
let ctx = CallContext {
|
||||
success: e.errors.is_empty(),
|
||||
kind,
|
||||
line,
|
||||
entry_index: entry_index + 1,
|
||||
call_entry_index: call_entry_index + 1,
|
||||
|
@ -68,6 +68,11 @@ pub fn new_failure_icon(id: &str) -> Element {
|
||||
new_icon(id, 512.px(), 512.px(), "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z", "red")
|
||||
}
|
||||
|
||||
/// Returns the SVG retry icon identified by id.
|
||||
pub fn new_retry_icon(id: &str) -> Element {
|
||||
new_icon(id, 512.px(), 512.px(), "M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z", "gold")
|
||||
}
|
||||
|
||||
/// Returns a SVG icon identified by `id`, with a `width` pixel by `height` pixel size, `path` and `color`.
|
||||
fn new_icon(id: &str, width: Pixel, height: Pixel, path: &str, color: &str) -> Element {
|
||||
let mut symbol = svg::new_symbol();
|
||||
|
@ -30,9 +30,9 @@ use crate::report::html::timeline::unit::{
|
||||
Byte, Interval, Microsecond, Millisecond, Pixel, Px, Scale, Second, TimeUnit,
|
||||
};
|
||||
use crate::report::html::timeline::util::{
|
||||
new_failure_icon, new_stripes, new_success_icon, trunc_str,
|
||||
new_failure_icon, new_retry_icon, new_stripes, new_success_icon, trunc_str,
|
||||
};
|
||||
use crate::report::html::timeline::{svg, CallContext, CALL_HEIGHT, CALL_INSET};
|
||||
use crate::report::html::timeline::{svg, CallContext, CallContextKind, CALL_HEIGHT, CALL_INSET};
|
||||
use crate::report::html::Testcase;
|
||||
|
||||
/// Returns the start and end date for these entries.
|
||||
@ -79,6 +79,8 @@ impl Testcase {
|
||||
root.add_child(elt);
|
||||
let elt = new_failure_icon("failure");
|
||||
root.add_child(elt);
|
||||
let elt = new_retry_icon("retry");
|
||||
root.add_child(elt);
|
||||
|
||||
// We add some space for the right last grid labels.
|
||||
let pixels_x = Interval::new(0.px(), width);
|
||||
@ -335,10 +337,10 @@ fn new_call_tooltip(
|
||||
|
||||
// Icon + URL + method
|
||||
let mut elt = svg::new_use();
|
||||
let icon = if call_ctx.success {
|
||||
"#success"
|
||||
} else {
|
||||
"#failure"
|
||||
let icon = match call_ctx.kind {
|
||||
CallContextKind::Success => "#success",
|
||||
CallContextKind::Failure => "#failure",
|
||||
CallContextKind::Retry => "#retry",
|
||||
};
|
||||
elt.add_attr(Href(icon.to_string()));
|
||||
elt.add_attr(X(x.0));
|
||||
@ -351,7 +353,10 @@ fn new_call_tooltip(
|
||||
let text = trunc_str(&text, 54);
|
||||
let text = format!("{text} {}", call.response.status);
|
||||
let mut elt = svg::new_text(x.0 + 30.0, y.0 + 16.0, &text);
|
||||
let color = if call_ctx.success { "#555" } else { "red" };
|
||||
let color = match call_ctx.kind {
|
||||
CallContextKind::Success | CallContextKind::Retry => "#555",
|
||||
CallContextKind::Failure => "red",
|
||||
};
|
||||
elt.add_attr(Fill(color.to_string()));
|
||||
elt.add_attr(FontWeight("bold".to_string()));
|
||||
group.add_child(elt);
|
||||
@ -436,30 +441,40 @@ fn new_call_tooltip(
|
||||
|
||||
// Run URL
|
||||
y += 56.px();
|
||||
let run = format!(
|
||||
let href = format!(
|
||||
"{}#e{}:c{}",
|
||||
call_ctx.run_filename, call_ctx.entry_index, call_ctx.call_entry_index
|
||||
);
|
||||
let mut elt = svg::new_text(x.0, y.0, "(view run)");
|
||||
elt.add_attr(Fill("royalblue".to_string()));
|
||||
elt.add_attr(TextDecoration("underline".to_string()));
|
||||
let mut a = new_a(&run);
|
||||
a.add_child(elt);
|
||||
group.add_child(a);
|
||||
let elt = new_link(x, y, "(view run)", &href);
|
||||
group.add_child(elt);
|
||||
|
||||
// Source URL
|
||||
let href = format!("{}#l{}", call_ctx.source_filename, call_ctx.line);
|
||||
let elt = new_link(x + 90.px(), y, "(view source)", &href);
|
||||
group.add_child(elt);
|
||||
|
||||
// Timings explanation
|
||||
y += delta_y;
|
||||
let run = format!("{}#l{}", call_ctx.source_filename, call_ctx.line);
|
||||
let mut elt = svg::new_text(x.0, y.0, "(view source)");
|
||||
elt.add_attr(Fill("royalblue".to_string()));
|
||||
elt.add_attr(TextDecoration("underline".to_string()));
|
||||
let mut a = new_a(&run);
|
||||
a.add_child(elt);
|
||||
group.add_child(a);
|
||||
let elt = new_link(
|
||||
x,
|
||||
y,
|
||||
"Explanation",
|
||||
"https://hurl.dev/docs/response.html#timings",
|
||||
);
|
||||
group.add_child(elt);
|
||||
|
||||
group
|
||||
}
|
||||
|
||||
fn new_link(x: Pixel, y: Pixel, text: &str, href: &str) -> Element {
|
||||
let mut elt = svg::new_text(x.0, y.0, text);
|
||||
elt.add_attr(Fill("royalblue".to_string()));
|
||||
elt.add_attr(TextDecoration("underline".to_string()));
|
||||
let mut a = new_a(href);
|
||||
a.add_child(elt);
|
||||
a
|
||||
}
|
||||
|
||||
/// Returns the highlighted span time of a call.
|
||||
fn new_call_sel(
|
||||
call: &Call,
|
||||
@ -472,8 +487,10 @@ fn new_call_sel(
|
||||
let offset_x_start = to_pixel(offset_x_start, scale_x);
|
||||
let offset_x_end = (call.timings.end_call - times.start).to_std().unwrap();
|
||||
let offset_x_end = to_pixel(offset_x_end, scale_x);
|
||||
|
||||
let color = if call_ctx.success { "green" } else { "red" };
|
||||
let color = match call_ctx.kind {
|
||||
CallContextKind::Success | CallContextKind::Retry => "green",
|
||||
CallContextKind::Failure => "red",
|
||||
};
|
||||
let mut elt = svg::new_rect(
|
||||
offset_x_start.0,
|
||||
0.0,
|
||||
|
Loading…
Reference in New Issue
Block a user