artifactview/templates/junit.hbs

183 lines
6.3 KiB
Handlebars

<div class="junit">
<div id="junit-suites">
<p class="coltitle">Test suites:</p>
<ul>
<li><button class="active">Show all</button></li>
{{#each suites.suites}}
<li data-status="{{#if errors == 0 && failures == 0}}success{{else}}failure{{/if}}"><button>{{name}}</button></li>
{{/each}}
</ul>
</div>
<div id="junit-cases">
<p class="coltitle">Test cases:</p>
<p id="junit-statusfilter" class="colsubtitle">
<button data-status="all">All <b>?</b></button>
<button data-status="success">OK <b>?</b></button>
<button data-status="failure">Failed <b>?</b></button>
<button data-status="error">Error <b>?</b></button>
<button data-status="flaky">Flaky <b>?</b></button>
<button data-status="skipped">Skipped <b>?</b></button>
</p>
<ul>
{{~#each suites.suites ~}}{{ let suite_name = &name }}
{{#each cases}}
<li data-suite="{{ suite_name }}" data-status="{{ status.id() }}">
<button>{{#if let Some(cn) = &classname}}<span>{{ cn }}::</span>{{/if}}{{ original_name }}</button>
<div class="pvcontent">
<h2><i class="gg-{{#if status.id() == "success"}}check-o{{else if status.id() == "skipped"}}block{{else if status.id() == "flaky"}}danger{{else}}close-o{{/if}}"></i> {{name}}</h2>
<p class="badges"><span>{{ this.status_txt() }}</span><span>{{ crate::util::time_to_ms(time) }}ms</span></p>
{{~#if let Some(msg) = status.message() ~}}
<pre><code>{{msg.message}}{{msg.text}}</code></pre>
{{~/if}}
{{~#if let Some(ref stdout) = system_out ~}}
<pre><code>{{stdout}}</code></pre>
{{~/if}}
{{~#if let Some(ref stderr) = system_err ~}}
<pre><code>{{stderr}}</code></pre>
{{~/if}}
{{~#each retries ~}}
<h3>Failed attempt #{{index}}</h3>
<p class="badges"><span>{{ crate::util::time_to_ms(time) }}ms</span></p>
{{~#if let Some(msg) = status.message() ~}}
<pre><code>{{msg.message}}{{msg.text}}</code></pre>
{{~/if}}
{{~#if let Some(ref stdout) = system_out ~}}
<pre><code>{{stdout}}</code></pre>
{{~/if}}
{{~#if let Some(ref stderr) = system_err ~}}
<pre><code>{{stderr}}</code></pre>
{{~/if}}
{{~/each}}
</div>
</li>
{{/each}}
{{~/each}}
</ul>
</div>
<div id="junit-preview">
<div id="preview-margin"></div>
<div class="prose">
<p class="light">Select a test case to show details</p>
</div>
</div>
</div>
<script>
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT
let preview;
let previewMargin;
let statusFilterBtns = {};
let filterSuite = null;
let filterStatus = "failure";
let lastScrollPos = 0;
let previewMarginH = 0;
document.addEventListener("DOMContentLoaded", () => {
preview = document.querySelector("#junit-preview > .prose");
previewMargin = document.getElementById("preview-margin");
statusFilterBtns = {};
const btnElms = document.getElementById("junit-statusfilter").children;
for (let i=0; i<btnElms.length; i++) {
const elm = btnElms[i];
statusFilterBtns[elm.attributes["data-status"].value] = elm;
}
document.querySelectorAll("#junit-suites li > button").forEach((btn) => {
btn.addEventListener("click", () => filterBySuite(btn));
});
document.querySelectorAll("#junit-statusfilter > button").forEach((btn) => {
btn.addEventListener("click", () => filterByStatus(btn));
});
document.querySelectorAll("#junit-cases li > button").forEach((btn) => {
btn.addEventListener("click", selectTestCase);
});
doFilter();
document.addEventListener("scroll", () => {
const delta = lastScrollPos - document.documentElement.scrollTop;
if (delta > 0) {
previewMarginH = Math.max(previewMarginH - delta, 0);
lastScrollPos = document.documentElement.scrollTop;
previewMargin.style.marginTop = previewMarginH + "px";
}
});
});
function setCls(elm, cls, val) {
if (val) elm.classList.add(cls);
else elm.classList.remove(cls);
}
function selectTestCase(event) {
let elm = event.target.closest("li");
const pvc = elm.querySelector(".pvcontent");
if (pvc && preview) {
preview.innerHTML = pvc.innerHTML;
preview.parentElement.setAttribute("data-status", elm.attributes["data-status"].value);
previewMarginH = Math.max(document.documentElement.scrollTop - 72, 20);
lastScrollPos = document.documentElement.scrollTop;
previewMargin.style.marginTop = previewMarginH + "px";
resetBtns("junit-cases ul");
elm.querySelector("button").classList.add("active");
if (window.innerWidth < 1000) preview.scrollIntoView();
}
}
function resetBtns(id) {
document.querySelectorAll(`#${id} .active`).forEach((elm) => {
elm.classList.remove("active");
});
}
function doFilter() {
const nStatus = {all: 0};
document.querySelectorAll("#junit-cases li").forEach((elm) => {
const status = elm.attributes["data-status"].value;
const isSuite = filterSuite === null || filterSuite === elm.attributes["data-suite"].value;
const vis = isSuite && (filterStatus === "all" || filterStatus === status);
setCls(elm, "hidden", !vis);
if (isSuite) {
nStatus[status] = nStatus[status] ? nStatus[status]+1 : 1;
nStatus.all++;
}
});
if (!nStatus[filterStatus]) {
filterStatus = "all";
doFilter();
return;
}
Object.entries(statusFilterBtns).forEach(([status, elm]) => {
const n = nStatus[status] ?? 0;
elm.children[0].textContent = n;
if (status === filterStatus) elm.classList.add("active");
else elm.classList.remove("active");
setCls(elm, "hidden", n === 0);
});
setCls(document.getElementById("junit-cases"), "filtered", filterSuite);
}
function filterBySuite(btn) {
const suite = btn.textContent;
if (suite) {
filterSuite = suite === "Show all" ? null : suite;
doFilter();
resetBtns("junit-suites");
btn.classList.add("active");
}
}
function filterByStatus(btn) {
filterStatus = btn.attributes["data-status"].value;
doFilter();
}
// @license-end
</script>