package web import ( "strings" "testing" ) func TestRankedBarsLabelEveryRowDirectly(t *testing.T) { // The label is the point of this chart: the question is which person or // which model, not what shape the distribution is. Rotated or truncated // axis labels would say the chart matters more than reading it. out := string(HBarChart([]Row{ {Label: "ilker@example.com", Value: 1200}, {Label: "deniz@example.com", Value: 300}, }, "--viz-1", "tokens")) for _, want := range []string{"ilker@example.com", "deniz@example.com", "1.2k", "300"} { if !strings.Contains(out, want) { t.Errorf("the chart does not show %q:\n%s", want, out) } } } func TestASmallValueStillDrawsAMark(t *testing.T) { // A bar that rounds to zero width reads as no data rather than as a small // number, which is a different and wrong claim. out := string(HBarChart([]Row{ {Label: "huge", Value: 1_000_000}, {Label: "tiny", Value: 1}, }, "--viz-1", "")) // Two bars, one per row, and the tiny one has a width. if n := strings.Count(out, `class="rk-bar"`); n != 2 { t.Errorf("drew %d marks for two rows", n) } if strings.Contains(out, "width:0.0%") { t.Error("the tiny value was drawn with no width") } } func TestAnEmptyChartSaysSoRatherThanRenderingNothing(t *testing.T) { // A missing chart reads as a broken page. out := string(HBarChart(nil, "--viz-1", "")) if !strings.Contains(out, "Nothing recorded") { t.Errorf("an empty series rendered as %q", out) } } func TestChartTextUsesTextColoursNotTheSeriesColour(t *testing.T) { // Values and labels wear text tokens; the mark beside them carries the // identity. Colouring the text too makes the palette do two jobs and reads // badly for anyone the hue separation was chosen to protect. out := string(HBarChart([]Row{{Label: "a", Value: 5}}, "--viz-2", "req")) if strings.Count(out, "var(--viz-2)") != 1 || !strings.Contains(out, `class="rk-bar"`) { t.Errorf("the series colour should be on the bar and nowhere else:\n%s", out) } } func TestEveryBarCarriesItsOwnHoverTitle(t *testing.T) { // The chart is in a browser, so it should answer a pointer. A does // that with no JavaScript, which is the right trade for a page this project // serves from a single binary. out := string(HBarChart([]Row{ {Label: "a", Value: 1}, {Label: "b", Value: 2}, }, "--viz-1", "tokens")) if n := strings.Count(out, `title="`); n != 2 { t.Errorf("%d hover titles for two bars", n) } } func TestALongLabelIsTrimmedRatherThanOverflowing(t *testing.T) { long := strings.Repeat("a", 80) + "@example.com" out := string(HBarChart([]Row{{Label: long, Value: 1}}, "--viz-1", "")) // Cut where it is drawn — the label cell ellipsises in CSS — and in full // where it is hovered, so the reader can still find out whose row it is. if !strings.Contains(out, `title="`+long+`: `) { t.Error("the full label is not available on hover") } css, err := staticFiles.ReadFile("static/base.css") if err != nil { t.Fatal(err) } if !strings.Contains(string(css), ".ranked .rk-label { font-size: .82rem; overflow: hidden; text-overflow: ellipsis;") { t.Error("the label cell no longer cuts a long label with an ellipsis") } } func TestASparklineOfNothingIsNotDrawn(t *testing.T) { // One point is not a trend, and a single dot invites reading one. if got := Sparkline([]float64{5}, ""); got != "" { t.Errorf("a one-point sparkline rendered: %q", got) } if got := Sparkline(nil, ""); got != "" { t.Errorf("an empty sparkline rendered: %q", got) } } func TestAFlatSparklineIsDrawnAlongTheBottom(t *testing.T) { // No traffic is a real answer. An empty box would read as a broken chart. out := string(Sparkline([]float64{0, 0, 0}, "")) if !strings.Contains(out, "<polyline") { t.Errorf("a flat series did not draw: %q", out) } } func TestTheChartScalesToItsContainer(t *testing.T) { // The dashboard is read on laptops and phones. A fixed pixel width is how a // table ends up scrolling sideways. out := string(HBarChart([]Row{{Label: "a", Value: 1}}, "", "")) // Bars are a share of whatever width the grid gives them, never pixels. if !strings.Contains(out, "width:100.0%") { t.Errorf("the bar is not sized as a share of its track:\n%s", out) } if strings.Contains(out, "px") { t.Error("a fixed pixel size was set on the chart") } } func TestTheRankedWrapperIsNotGivenAFixedHeight(t *testing.T) { // This chart grows a row at a time, so whatever class it wears must not be // one the stylesheet caps. It wore "chart", which is the fixed-height box // the time-series charts live in, and the bars spilled over the table // underneath them. out := string(HBarChart([]Row{{Label: "a", Value: 1}}, "", "")) class := "" // the wrapper's class, read back out of the markup if i := strings.Index(out, `<div class="`); i >= 0 { rest := out[i+len(`<div class="`):] class = rest[:strings.Index(rest, `"`)] } if class == "" { t.Fatalf("no wrapper to check:\n%s", out) } css, err := staticFiles.ReadFile("static/base.css") if err != nil { t.Fatal(err) } for _, line := range strings.Split(string(css), "\n") { decl, ok := strings.CutPrefix(strings.TrimSpace(line), "."+class+" ") if !ok || !strings.HasPrefix(decl, "{") { continue } if strings.Contains(decl, "height:") && !strings.Contains(decl, "height: auto") { t.Errorf("%q is capped by the stylesheet, so taller charts overflow it: %s", class, strings.TrimSpace(line)) } } } func TestLatencyReadsAsAPersonWouldSayIt(t *testing.T) { for ms, want := range map[float64]string{5: "5 ms", 8825: "8.8 s", 110695: "1m 50s", 225634: "3m 45s"} { if got := humanLatency(ms); got != want { t.Errorf("%v ms: got %q, want %q", ms, got, want) } } }