<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>Test Report - {{ report.time_report_generation }}</title>
    <!-- Custom styles, embedded to produce single file report -->
    <style type="text/css">
        {% include "style.css" %}
    </style>
    <!--<link href="style.css" rel="stylesheet" type="text/css"/>-->
</head>
<body onLoad="init()">
<script>

    function toArray(iter) {
        if (iter === null) {
            return null;
        }
        return Array.prototype.slice.call(iter);
    }

    function find(selector, elem) {
        if (!elem) {
            elem = document;
        }
        return elem.querySelector(selector);
    }

    function find_all(selector, elem) {
        if (!elem) {
            elem = document;
        }
        return toArray(elem.querySelectorAll(selector));
    }

    function sort_column(elem) {
        toggle_sort_states(elem);
        var colIndex = toArray(elem.parentNode.childNodes).indexOf(elem);
        var key;
        if (elem.classList.contains('numeric')) {
            key = key_num;
        } else if (elem.classList.contains('result')) {
            key = key_result;
        } else {
            key = key_alpha;
        }
        sort_table(elem, key(colIndex));
    }

    function show_all_extras() {
        find_all('.col-result').forEach(show_extras);
    }

    function hide_all_extras() {
        find_all('.col-result').forEach(hide_extras);
    }

    function show_extras(colresult_elem) {
        var extras = colresult_elem.parentNode.nextElementSibling;
        var expandcollapse = colresult_elem.firstElementChild;
        extras.classList.remove("collapsed");
        expandcollapse.classList.remove("expander");
        expandcollapse.classList.add("collapser");
    }

    function hide_extras(colresult_elem) {
        var extras = colresult_elem.parentNode.nextElementSibling;
        var expandcollapse = colresult_elem.firstElementChild;
        extras.classList.add("collapsed");
        expandcollapse.classList.remove("collapser");
        expandcollapse.classList.add("expander");
    }

    function show_filters() {
        var filter_items = document.getElementsByClassName('filter');
        for (var i = 0; i < filter_items.length; i++)
            filter_items[i].hidden = false;
    }

    function add_collapse() {
        // Add links for show/hide all
        var resulttable = find('table#results-table');
        var showhideall = document.createElement("p");
        showhideall.innerHTML = '<a href="javascript:show_all_extras()">Show all details</a> / ' +
            '<a href="javascript:hide_all_extras()">Hide all details</a>';
        resulttable.parentElement.insertBefore(showhideall, resulttable);

        // Add show/hide link to each result
        find_all('.col-result').forEach(function (elem) {
            var collapsed = get_query_parameter('collapsed') || 'Passed';
            var extras = elem.parentNode.nextElementSibling;
            var expandcollapse = document.createElement("span");
            if (collapsed.includes(elem.innerHTML)) {
                extras.classList.add("collapsed");
                expandcollapse.classList.add("expander");
            } else {
                expandcollapse.classList.add("collapser");
            }
            elem.appendChild(expandcollapse);

            elem.addEventListener("click", function (event) {
                if (event.currentTarget.parentNode.nextElementSibling.classList.contains("collapsed")) {
                    show_extras(event.currentTarget);
                } else {
                    hide_extras(event.currentTarget);
                }
            });
        })
    }

    function get_query_parameter(name) {
        var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
        return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
    }

    function init() {
        reset_sort_headers();

        add_collapse();

        show_filters();

        toggle_sort_states(find('.initial-sort'));

        find_all('.sortable').forEach(function (elem) {
            elem.addEventListener("click",
                function (event) {
                    sort_column(elem);
                }, false)
        });

    };

    function sort_table(clicked, key_func) {
        var rows = find_all('.results-table-row');
        var reversed = !clicked.classList.contains('asc');
        var sorted_rows = sort(rows, key_func, reversed);
        /* Whole table is removed here because browsers acts much slower
         * when appending existing elements.
         */
        var thead = document.getElementById("results-table-head");
        document.getElementById('results-table').remove();
        var parent = document.createElement("table");
        parent.id = "results-table";
        parent.appendChild(thead);
        sorted_rows.forEach(function (elem) {
            parent.appendChild(elem);
        });
        document.getElementsByTagName("BODY")[0].appendChild(parent);
    }

    function sort(items, key_func, reversed) {
        var sort_array = items.map(function (item, i) {
            return [key_func(item), i];
        });
        var multiplier = reversed ? -1 : 1;

        sort_array.sort(function (a, b) {
            var key_a = a[0];
            var key_b = b[0];
            return multiplier * (key_a >= key_b ? 1 : -1);
        });

        return sort_array.map(function (item) {
            var index = item[1];
            return items[index];
        });
    }

    function key_alpha(col_index) {
        return function (elem) {
            return elem.childNodes[1].childNodes[col_index].firstChild.data.toLowerCase();
        };
    }

    function key_num(col_index) {
        return function (elem) {
            return parseFloat(elem.childNodes[1].childNodes[col_index].firstChild.data);
        };
    }

    function key_result(col_index) {
        return function (elem) {
            var strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed',
                'Skipped', 'Passed'];
            return strings.indexOf(elem.childNodes[1].childNodes[col_index].firstChild.data);
        };
    }

    function reset_sort_headers() {
        find_all('.sort-icon').forEach(function (elem) {
            elem.parentNode.removeChild(elem);
        });
        find_all('.sortable').forEach(function (elem) {
            var icon = document.createElement("div");
            icon.className = "sort-icon";
            icon.textContent = "vvv";
            elem.insertBefore(icon, elem.firstChild);
            elem.classList.remove("desc", "active");
            elem.classList.add("asc", "inactive");
        });
    }

    function toggle_sort_states(elem) {
        //if active, toggle between asc and desc
        if (elem.classList.contains('active')) {
            elem.classList.toggle('asc');
            elem.classList.toggle('desc');
        }

        //if inactive, reset all other functions and add ascending active
        if (elem.classList.contains('inactive')) {
            reset_sort_headers();
            elem.classList.remove('inactive');
            elem.classList.add('active');
        }
    }

    function is_all_rows_hidden(value) {
        return value.hidden == false;
    }

    function filter_table(elem) {
        var outcome_att = "data-test-result";
        var outcome = elem.getAttribute(outcome_att);
        class_outcome = outcome + " results-table-row";
        var outcome_rows = document.getElementsByClassName(class_outcome);

        for (var i = 0; i < outcome_rows.length; i++) {
            outcome_rows[i].hidden = !elem.checked;
        }

        var rows = find_all('.results-table-row').filter(is_all_rows_hidden);
        var all_rows_hidden = rows.length == 0 ? true : false;
        var not_found_message = document.getElementById("not-found-message");
        not_found_message.hidden = !all_rows_hidden;
    }
</script>

<h1>Test Report - {{ report.time_report_generation }}</h1>
<p>Report generated on {{ report.time_report_generation }} by <a href="https://github.com/g-bon/pytest-jinja">pytest-jinja</a></p>
<h2>Environment</h2>
<table id="environment">
    {% for name,value in report.environment.items() %}
        <tr>
            <td>{{ name }}</td>
            <td>{{ value }}</td>
        </tr>
    {% endfor %}
</table>
<h2>Summary</h2>
<p>{{ report.tests_count }} tests ran in {{ report.duration | round(2)}} seconds. </p>
<p class="filter" hidden="true">(Un)check the boxes to filter the results.</p>
<input checked="true" class="filter" data-test-result="passed"
       {{ 'disabled' if not report.passed }} hidden="true"
       name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/>
<span class="passed">{{ report.passed }} passed</span>,

<input checked="true" class="filter" data-test-result="skipped"
       {{ 'disabled' if not report.skipped }} hidden="true"
       name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/>
<span class="skipped">{{ report.skipped }} skipped</span>,

<input checked="true" class="filter" data-test-result="failed"
       {{ 'disabled' if not report.failed }} hidden="true"
       name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/>
<span class="failed">{{ report.failed }} failed</span>,

<input checked="true" class="filter" data-test-result="error"
       {{ 'disabled' if not report.errors }} hidden="true"
       name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/>
<span class="error">{{ report.errors }} errors</span>,

<input checked="true" class="filter" data-test-result="xfailed"
       {{ 'disabled' if not report.pxfailedassed }} hidden="true"
       name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/>
<span class="xfailed">{{ report.xfailed }} expected failures</span>

<input checked="true" class="filter" data-test-result="xpassed"
       {{ 'disabled' if not report.xpassed }} hidden="true"
       name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/>
<span class="xpassed">{{ report.xpassed }} unexpected passes</span>

<h2>Results</h2>
<table id="results-table">
    <thead id="results-table-head">
    <tr>
        <th class="sortable result initial-sort" col="result">Result</th>
        <th>Test</th>
        <th class="sortable numeric" col="duration">Duration</th>
    </tr>
    <tr hidden="true" id="not-found-message">
        <th colspan="5">No results found. Try to check the filters</th>
    </tr>
    </thead>
    {% for r in report.results %}
        <tbody class="{{ r.outcome }} results-table-row">
            <tr>
                <td class="col-result">{{ r.outcome }}</td>
                <td>{{ r.test_id }}</td>
                <td class="col-duration">{{ r.time|round(2) }}s</td>
            </tr>
            <tr>
                <td class="extra" colspan="5">
                    <div class="{{ 'log' if r.stacktrace else 'empty log' }}">{{ r.stacktrace if r.stacktrace else "No log output captured."}}</div>
                </td>
            </tr>
        </tbody>
    {% endfor %}
</table>
</body>
</html>

