OXYGENHUB
# 7dbb41b09a99 oxygenhub / templates / tests.html
7.6 KiB · HTML · last changed Tests: a test's history, and a tests check naming what broke (part 3) 1 month ago source blame history raw
36e5a9c1 Andy McCall ↶ 1
{% extends "base.html" %}
2
{% import "repo_tabs.html" as m %}
3
{% block title %}Tests · {{ repo.owner }}/{{ repo.name }}{% endblock %}
4
{% block tabs %}{% call m::tabs(repo, "tests") %}{% endblock %}
5
{% block content %}
6
{% if let Some(r) = report %}
7
<section class="cov-hero">
8
  <div class="cov-hero-head">
9
    <div>
10
      <h1>Tests</h1>
11
      <div class="cov-sub">{% if bookmarks.len() > 1 %}<form class="inline" method="get" action="{{ repo.url }}/tests"><label for="t-bm" class="mute">bookmark</label> <select id="t-bm" name="bookmark" onchange="this.form.submit()">{% for b in bookmarks %}<option value="{{ b }}"{% if b.as_str() == bookmark.as_str() %} selected{% endif %}>{{ b }}</option>{% endfor %}</select> <noscript><button>Show</button></noscript></form>{% else %}<span class="mute">on</span> <strong>{{ bookmark }}</strong>{% endif %}
12
        · <a href="{{ repo.url }}/flow/{{ r.run_number }}">run #{{ r.run_number }}</a> · job {{ r.job }} · <time title="{{ r.created_ms|when_i }}">{{ r.created_ms|ago_i }}</time></div>
13
    </div>
14
    <div class="cov-score {{ self.class_of(r) }}">
15
      <div class="cov-big">{% if r.broken() > 0 %}{{ r.broken() }} failing{% else %}{{ self.rate(r) }}{% endif %}</div>
16
      <div class="cov-change same">{% if r.broken() > 0 %}{{ self.rate(r) }} of {{ r.tests - r.skipped }} passed{% else %}of {{ r.tests - r.skipped }} tests passed{% endif %}</div>
17
    </div>
18
  </div>
19
  {% if runs > 1 %}<div class="cov-hero-chart">{{ rate_chart|safe }}</div>{% endif %}
20
</section>
21
22
<section class="cov-facts">
23
  <div><span class="k">Tests</span><span class="v">{{ r.tests }}{% if let Some(d) = self.count_delta() %} <small>{{ self.signed(d) }}</small>{% endif %}</span></div>
24
  <div><span class="k">Passed</span><span class="v cov-good">{{ r.passed }}</span></div>
25
  <div><span class="k">Failed</span><span class="v{% if r.failed > 0 %} cov-poor{% endif %}">{{ r.failed }}</span></div>
26
  {% if r.errors > 0 %}<div><span class="k">Errored</span><span class="v cov-poor">{{ r.errors }}</span></div>{% endif %}
27
  <div><span class="k">Skipped</span><span class="v">{{ r.skipped }}</span></div>
28
  <div><span class="k">Test time</span><span class="v">{{ self.time(r.time_ms) }}{% if let Some(d) = self.time_delta() %} <small>{{ d }}</small>{% endif %}</span></div>
29
  {% if let Some(t) = first_ms %}<div><span class="k">Reporting since</span><span class="v small"><time title="{{ t|when_i }}">{{ t|ago_i }}</time></span></div>{% endif %}
30
</section>
31
32
<div class="cov-grid">
33
  <section class="panel cov-card">
34
    <h2>This run</h2>
35
    <ul class="cov-stats">
36
      <li><strong>{{ r.passed }}</strong> of <strong>{{ r.tests - r.skipped }}</strong> tests passed <span class="{{ self.class_of(r) }}">({{ self.rate(r) }})</span>{% if r.skipped > 0 %}, {{ r.skipped }} skipped{% endif %}</li>
37
      {% if let Some(p) = previous %}<li>Against <a href="{{ repo.url }}/tests?report={{ p.id }}">run #{{ p.run_number }}</a>: {{ broke.len() }} newly failing, {{ fixed.len() }} fixed{% if added > 0 %}, {{ added }} new tests{% endif %}</li>{% else %}<li>The first report of job {{ r.job }} here</li>{% endif %}
38
    </ul>
39
    {% if !broke.is_empty() %}<h3>Newly failing</h3>
40
    <ul class="t-list">{% for c in broke %}<li><span class="cov-poor">✕</span> <code>{{ c.title }}</code></li>{% endfor %}</ul>{% endif %}
41
    {% if !fixed.is_empty() %}<h3>Fixed</h3>
42
    <ul class="t-list">{% for c in fixed %}<li><span class="cov-good">✓</span> <code>{{ c.title }}</code></li>{% endfor %}</ul>{% endif %}
43
  </section>
44
  <section class="panel cov-card">
45
    <h2>Flaky <small class="mute">failed and passed in the last {{ flaky_runs }} runs</small></h2>
46
    {% if flaky.is_empty() %}<p class="mute small">None: every test kept its result.</p>{% else %}
3d5c825a Andy McCall ↶ 47
    <table class="cov-moved">{% for f in flaky %}<tr><td><a href="{{ repo.url }}/tests/case?classname={{ f.classname|urlencode }}&name={{ f.name|urlencode }}&bookmark={{ bookmark|urlencode }}"><code>{{ f.title() }}</code></a></td><td class="num"><span class="cov-poor">{{ f.failed }} ✕</span> <span class="cov-good">{{ f.passed }} ✓</span></td></tr>{% endfor %}</table>{% endif %}
36e5a9c1 Andy McCall ↶ 48
  </section>
49
</div>
50
51
{% if !failures.is_empty() %}<section class="panel">
52
  <h2>Failures <small class="mute">{{ r.broken() }} in run #{{ r.run_number }}{% if r.broken() > failures.len() as i64 %}, the first {{ failures.len() }} shown{% endif %}</small></h2>
53
  <div class="flow-tests">{% for f in failures %}<details class="tests-failure">
3d5c825a Andy McCall ↶ 54
    <summary><span class="bad">{% if f.status == "error" %}!{% else %}✕{% endif %}</span> <code>{{ f.title() }}</code> <a class="small" href="{{ repo.url }}/tests/case?classname={{ f.classname|urlencode }}&name={{ f.name|urlencode }}&bookmark={{ bookmark|urlencode }}">history</a>{% if !f.message.is_empty() %} <span class="small mute">{{ f.message }}</span>{% endif %}</summary>
36e5a9c1 Andy McCall ↶ 55
    {% if !f.output.is_empty() %}<pre class="flow-log">{{ f.output }}</pre>{% endif %}
56
  </details>{% endfor %}</div>
57
</section>{% endif %}
58
59
{% if runs > 1 %}<section class="panel">
60
  <h2>Tests run <small class="mute"><span class="cov-key covered">passed</span> <span class="cov-key relevant">failed</span></small></h2>
61
  {{ count_chart|safe }}
62
</section>{% endif %}
63
64
<section class="panel">
65
  <h2>Slowest tests <small class="mute">in run #{{ r.run_number }}</small></h2>
66
  <div class="table"><table class="cov-files">
67
  <tr><th>Test</th><th class="num">Time</th></tr>
3d5c825a Andy McCall ↶ 68
  {% for c in slowest %}<tr><td><a href="{{ repo.url }}/tests/case?classname={{ c.classname|urlencode }}&name={{ c.name|urlencode }}&bookmark={{ bookmark|urlencode }}"><code>{{ c.title() }}</code></a>{% if c.status != "passed" %} <span class="cov-poor small">{{ c.status }}</span>{% endif %}</td><td class="num">{{ self.time(c.time_ms) }}</td></tr>{% endfor %}
36e5a9c1 Andy McCall ↶ 69
  </table></div>
70
</section>
71
72
{% if recent.len() > 1 %}<section class="panel">
73
  <h2>Recent runs on {{ bookmark }}</h2>
74
  <div class="table"><table class="cov-runs">
75
  <tr><th>Run</th><th>Job</th><th>Passing</th><th class="num">Tests</th><th class="num">Failed</th><th class="num">Time</th><th>When</th></tr>
76
  {% for x in recent %}<tr{% if x.report.id == r.id %} class="on"{% endif %}>
77
    <td><a href="{{ repo.url }}/tests?report={{ x.report.id }}">#{{ x.report.run_number }}</a></td>
78
    <td>{{ x.report.job }}</td>
79
    <td><span class="cov-bar {{ self.class_of(x.report) }}"><span style="width: {% if let Some(p) = crate::web::test_reports::pass_rate(x.report) %}{{ p }}{% else %}0{% endif %}%"></span></span> {{ self.rate(x.report) }}</td>
80
    <td class="num">{{ x.report.tests }}{% if let Some(d) = x.delta %}{% if *d != 0 %} <small class="mute">{{ self.signed(d) }}</small>{% endif %}{% endif %}</td>
81
    <td class="num">{% if x.report.broken() > 0 %}<span class="cov-poor">{{ x.report.broken() }}</span>{% else %}0{% endif %}</td>
82
    <td class="num">{{ self.time(x.report.time_ms) }}</td>
83
    <td class="small"><time title="{{ x.report.created_ms|when_i }}">{{ x.report.created_ms|ago_i }}</time></td>
84
  </tr>{% endfor %}
85
  </table></div>
86
</section>{% endif %}
87
{% else %}
88
<div class="head"><h1>Tests <small>of {{ bookmark }}</small></h1></div>
89
<section class="panel">
90
  <p>No test report for <strong>{{ bookmark }}</strong> yet.</p>
91
  <p>Give a job in <code>.flow/pipeline.kdl</code> an artifact named <code>junit</code> holding a JUnit XML report, kept with <code>when "always"</code> so a failing run still sends it. For pytest:</p>
92
<pre>job "test" {
93
    sandbox "python"
94
    exec "pytest" "--junitxml=/src/out/junit.xml"
95
    output {
96
        artifact "junit" {
97
            path "/src/out/junit.xml"
98
            when "always"
99
        }
100
    }
101
}</pre>
102
  <p class="small mute">cargo-nextest (a <code>junit</code> profile), Jest's junit reporter and gotestsum write JUnit XML too.</p>
103
</section>
104
{% endif %}
105
{% endblock %}