{%- set s = test_summary %} {%- set executed = s.total - s.skipped %}

{{ test_report_title|e }}

Generated {{ timestamp }}{% if duration is not none %} · Total time {{ '%.2f'|format(duration) }}s{% endif %}
Total
{{ s.total }}
Passed
{{ s.passed }}
Failed
{{ s.failed }}
Errors
{{ s.error }}
Skipped
{{ s.skipped }}
Pass rate
{{ s.percentage }}%

Outcome

{%- set t = s.total if s.total else 1 %} {%- set p1 = (s.passed * 100 / t) %}{% set p2 = p1 + (s.failed * 100 / t) %}{% set p3 = p2 + (s.error * 100 / t) %}
Passed Failed Error Skipped

Environment

{% for key, value in (environment or {}).items() %}{% endfor %}
{{ key|e }}{{ value|e }}
Executed{{ executed }} of {{ s.total }} (skipped excluded from pass rate)

Slowest tests

{% if slowest_tests %} {% for t in slowest_tests %}{% endfor %}
{{ t.name|truncate(48, True)|e }}{{ '%.3f'|format(t.duration) }}s
{% else %}

No timing data recorded.

{% endif %}
{% set cmp = comparison %} {% if cmp %} {%- set base = cmp.baseline or {} %} {%- macro name_list(title, names) -%} {% if names %}
{{ title }} ({{ names|length }})
{% endif %} {%- endmacro %}

Compared with previous run

{{ (base.source or 'baseline')|e }}{% if base.timestamp %} · {{ base.timestamp|e }}{% endif %}{% if base.percentage is number %} · pass rate then {{ base.percentage|e }}%{% endif %}{% if cmp.percentage_delta is number %} · {{ '%+.2f'|format(cmp.percentage_delta) }} pts{% endif %}
New failures{{ cmp.new_failures|length }}
Fixed{{ cmp.fixed|length }}
Still failing{{ cmp.still_failing|length }}
New tests{{ cmp.new_tests|length }}
Removed tests{{ cmp.removed_tests|length }}
Slower{{ cmp.slower|length }}
{{ name_list('New failures', cmp.new_failures) }} {{ name_list('Fixed', cmp.fixed) }} {{ name_list('Still failing', cmp.still_failing) }} {{ name_list('New tests', cmp.new_tests) }} {{ name_list('Removed tests', cmp.removed_tests) }} {% if cmp.slower %}
Slower tests ({{ cmp.slower|length }})
    {% for t in cmp.slower %}
  • {{ t.name|e }}: {{ '%.3f'|format(t.before) }}s → {{ '%.3f'|format(t.after) }}s (×{{ t.ratio|e }})
  • {% endfor %}
{% endif %}
{% endif %}
{% for r in test_results %} {%- set md = r.metadata or {} %} {%- set change = (cmp.changes or {}).get(r.name) if cmp else none %}
{{ r.result }} {% if change %}{{ change|replace('-', ' ')|e }}{% endif %} {{ r.name|e }}{% for tag in md.get('tags') or [] %}{{ tag|e }}{% endfor %} {% if md.get('duration') is number %}{{ '%.3f'|format(md.duration) }}s{% endif %}
{% if md.get('location') %}

{{ md.location|e }}{% if md.get('project') %} · {{ md.project|e }}{% endif %}{% if md.get('retries') %} · retries: {{ md.retries|e }}{% endif %}{% if md.get('flaky') %} · flaky{% endif %}

{% endif %}

Description

{% if r.description %}

{{ r.description|e }}

{% else %}

No description (add a docstring to the test).

{% endif %} {% if r.traceback %}

{% if r.result in ['failed', 'error'] %}Error{% else %}Details{% endif %}

{{ r.traceback|e }}
{% endif %} {% set attachments = md.get('attachments') or [] %} {% if attachments %}

Attachments

{% for a in attachments %} {%- set ctype = (a.content_type or 'application/octet-stream')|string %} {%- set safe_path = a.path and not (a.path|string|trim|lower).startswith(('javascript:', 'vbscript:', 'data:')) %} {% if ctype.startswith('image/') and a.data %}
{{ a.name|e }}
{{ a.name|e }}
{% elif a.text %}
{{ a.name|e }}
{{ a.text|e }}
{% elif a.data %}

{{ a.name|e }} ({{ ctype|e }})

{% elif safe_path %}

{{ a.name|e }} ({{ ctype|e }})

{% endif %} {% endfor %}
{% endif %}
{% endfor %}

No tests match the current filter.