Merge branch 'initial-frontend' into next-gen

This commit is contained in:
Jim Brännlund 2021-05-09 22:00:34 +02:00
commit 68c72c7223
11 changed files with 1438 additions and 332 deletions

View File

@ -6,36 +6,34 @@ $border-width: 1px;
$spacing: 5px;
body {
font-family: Helvetica, Arial, sans-serif;
font-size: $font-size-text;
/* do not increase min-width as some may use split screens */
min-width: 800px;
color: #999;
font-family: Helvetica, Arial, sans-serif;
font-size: $font-size-text;
/* do not increase min-width as some may use split screens */
min-width: 800px;
color: #999;
}
h1 {
font-size: $font-size-h1;
color: black;
font-size: $font-size-h1;
color: black;
}
h2 {
font-size: $font-size-h2;
color: black;
font-size: $font-size-h2;
color: black;
}
p {
color: black;
color: black;
}
a {
color: #999;
color: #999;
}
table {
border-collapse: collapse;
border-collapse: collapse;
}
/******************************
@ -43,14 +41,19 @@ table {
******************************/
#environment {
td {
padding: $spacing;
border: $border-width solid #E6E6E6;
}
td {
padding: $spacing;
border: $border-width solid #e6e6e6;
vertical-align: top;
}
tr:nth-child(odd) {
background-color: #f6f6f6;
}
tr:nth-child(odd) {
background-color: #f6f6f6;
}
ul {
margin: 0;
padding: 0 20px;
}
}
/******************************
@ -58,7 +61,7 @@ table {
******************************/
span.passed,
.passed .col-result {
color: green;
color: green;
}
span.skipped,
@ -67,7 +70,7 @@ span.rerun,
.skipped .col-result,
.xfailed .col-result,
.rerun .col-result {
color: orange;
color: orange;
}
span.error,
@ -76,9 +79,15 @@ span.xpassed,
.error .col-result,
.failed .col-result,
.xpassed .col-result {
color: red;
color: red;
}
.col-result {
text-transform: capitalize;
}
.col-links__extra {
margin-right: 3px;
}
/******************************
* RESULTS TABLE
@ -94,21 +103,21 @@ span.xpassed,
*------------------*/
#results-table {
border: $border-width solid #e6e6e6;
color: #999;
font-size: $font-size-text;
width: 100%;
border: $border-width solid #e6e6e6;
color: #999;
font-size: $font-size-text;
width: 100%;
th,
td {
padding: $spacing;
border: $border-width solid #E6E6E6;
text-align: left;
}
th,
td {
padding: $spacing;
border: $border-width solid #e6e6e6;
text-align: left;
}
th {
font-weight: bold;
}
th {
font-weight: bold;
}
}
/*------------------
@ -119,99 +128,159 @@ $extra-height: 240px;
$extra-media-width: 320px;
.log {
background-color: #e6e6e6;
border: $border-width solid #e6e6e6;
color: black;
display: block;
font-family: "Courier New", Courier, monospace;
height: $extra-height - 2 * $spacing;
overflow-y: scroll;
padding: $spacing;
white-space: pre-wrap;
background-color: #e6e6e6;
border: $border-width solid #e6e6e6;
color: black;
display: block;
font-family: 'Courier New', Courier, monospace;
height: $extra-height - 2 * $spacing;
overflow-y: scroll;
padding: $spacing;
white-space: pre-wrap;
&:only-child {
height: inherit;
}
&:only-child {
height: inherit;
}
}
div.image {
border: $border-width solid #e6e6e6;
float: right;
height: $extra-height;
margin-left: $spacing;
overflow: hidden;
width: $extra-media-width;
border: $border-width solid #e6e6e6;
float: right;
height: $extra-height;
margin-left: $spacing;
overflow: hidden;
width: $extra-media-width;
img {
width: $extra-media-width;
}
img {
width: $extra-media-width;
}
}
div.video {
border: $border-width solid #e6e6e6;
float: right;
height: $extra-height;
margin-left: $spacing;
overflow: hidden;
width: $extra-media-width;
border: $border-width solid #e6e6e6;
float: right;
height: $extra-height;
margin-left: $spacing;
overflow: hidden;
width: $extra-media-width;
video {
overflow: hidden;
width: $extra-media-width;
height: $extra-height;
}
video {
overflow: hidden;
width: $extra-media-width;
height: $extra-height;
}
}
.collapsed {
display: none;
display: none;
}
.expander::after {
content: " (show details)";
color: #BBB;
font-style: italic;
cursor: pointer;
content: ' (show details)';
color: #bbb;
font-style: italic;
cursor: pointer;
}
.collapser::after {
content: " (hide details)";
color: #BBB;
font-style: italic;
cursor: pointer;
content: ' (hide details)';
color: #bbb;
font-style: italic;
cursor: pointer;
}
/*------------------
* 3. Sorting items
*------------------*/
.sortable {
cursor: pointer;
cursor: pointer;
&.asc {
&:after {
content: ' ';
position: relative;
left: 5px;
bottom: -12.5px;
border: 10px solid #4caf50;
border-bottom: 0;
border-left-color: transparent;
border-right-color: transparent;
}
}
&.desc {
&:after {
content: ' ';
position: relative;
left: 5px;
bottom: 12.5px;
border: 10px solid #4caf50;
border-top: 0;
border-left-color: transparent;
border-right-color: transparent;
}
}
}
.sort-icon {
font-size: 0px;
float: left;
margin-right: $spacing;
margin-top: $spacing;
// font-size: 0px;
// float: left;
// margin-right: $spacing;
// margin-top: $spacing;
/*triangle*/
$triangle-width: 8px;
width: 0;
height: 0;
border-left: $triangle-width solid transparent;
border-right: $triangle-width solid transparent;
// /*triangle*/
// $triangle-width: 8px;
// width: 0;
// height: 0;
// border-left: $triangle-width solid transparent;
// border-right: $triangle-width solid transparent;
.inactive & {
/*finish triangle*/
border-top: $triangle-width solid #E6E6E6;
}
// .asc {
// /*finish triangle*/
// border-bottom: $triangle-width solid #999;
// }
.asc.active & {
/*finish triangle*/
border-bottom: $triangle-width solid #999;
}
.desc.active & {
/*finish triangle*/
border-top: $triangle-width solid #999;
}
// .desc {
// /*finish triangle*/
// border-top: $triangle-width solid #999;
// }
}
.hidden {
display: none;
}
.summary {
display: flex;
&__data {
flex: 0 0 550px;
}
&__reload {
flex: 1 1;
display: flex;
justify-content: center;
&__button {
flex: 0 0 300px;
display: flex;
color: white;
font-weight: bold;
background-color: #4caf50;
text-align: center;
justify-content: center;
align-items: center;
border-radius: 3px;
cursor: pointer;
&.hidden {
@extend .hidden;
}
&:hover {
background-color: #46a049;
}
}
}
&__spacer {
flex: 0 0 550px;
}
}
input.filter {
margin-left: 10px;
}

View File

@ -0,0 +1,5 @@
{
"tabWidth": 2,
"useTabs": false,
"singleQuote": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -0,0 +1,82 @@
// const templateEnvRow = find('#template_environment_row');
// const templateResult = find('#template_results-table__tbody');
// const aTag = find('#template_a');
// const aTagImg = find('#template_img');
// const listHeader = find('#template_results-table__head');
const dom = {
getStaticRow: (key, value) => {
var envRow = templateEnvRow.content.cloneNode(true);
const isObj = typeof value === 'object' && value !== null;
const values = isObj
? Object.keys(value).map((k) => `${k}: ${value[k]}`)
: null;
const valuesElement = htmlToElements(
values
? `<ul>${values.map((val) => `<li>${val}</li>`).join('')}<ul>`
: `<div>${value}</div>`
)[0];
var td = findAll('td', envRow);
td[0].textContent = key;
td[1].appendChild(valuesElement);
return envRow;
},
getListHeader: () => {
const header = listHeader.content.cloneNode(true);
const sortAttr = localStorage.getItem('sort');
const sortAsc = JSON.parse(localStorage.getItem('sortAsc'));
const sortables = ['outcome', 'nodeid', 'duration'];
sortables.forEach((sortCol) => {
if (sortCol === sortAttr) {
find(`[data-column-type="${sortCol}"]`, header).classList.add(
sortAsc ? 'desc' : 'asc'
);
}
});
return header;
},
getListHeaderEmpty: () => listHeaderEmpty.content.cloneNode(true),
getResultTBody: ({ nodeid, longrepr, extras, duration }, outcome) => {
const isFail = outcome === 'failed';
const resultBody = templateResult.content.cloneNode(true);
find('tbody', resultBody).classList.add(outcome);
find('.col-result', resultBody).innerText = outcome;
find('.col-name', resultBody).innerText = nodeid;
find('.col-duration', resultBody).innerText = `${(duration * 1000).toFixed(
2
)}s`;
if (isFail) {
find('.log', resultBody).innerText = longrepr
? longrepr.reprtraceback.reprentries[0].data.lines.join('\n')
: '';
} else {
find('.extras-row', resultBody).classList.add('hidden');
}
extras &&
extras.forEach(({ name, format_type, content }) => {
const extraLink = aTag.content.cloneNode(true);
const extraLinkItem = find('a', extraLink);
const folderItems = ['image', 'video', 'text', 'html', 'json'];
extraLinkItem.href = `${
folderItems.includes(format_type) ? 'assets/' : ''
}${content}`;
extraLinkItem.className = `col-links__extra ${format_type}`;
extraLinkItem.innerText = name;
find('.col-links', resultBody).appendChild(extraLinkItem);
if (format_type === 'image') {
const imgElTemp = aTagImg.content.cloneNode(true);
find('a', imgElTemp).href = `assets/${content}`;
find('img', imgElTemp).src = `assets/${content}`;
find('.extra .image', resultBody).appendChild(imgElTemp);
}
});
return resultBody;
},
};

View File

@ -0,0 +1,46 @@
const dispatchEvent = (type, detail) => {
const newEvent = new CustomEvent(type, { detail });
document.dispatchEvent(newEvent);
};
const doSort = (type) => {
const newSortType = localStorage.getItem('sort') !== type;
const currentAsc = JSON.parse(localStorage.getItem('sortAsc'));
const ascending = newSortType ? true : !currentAsc;
localStorage.setItem('sort', type);
localStorage.setItem('sortAsc', ascending);
dispatchEvent('sort', { type, ascending });
};
document.addEventListener('sort', (e) => {
const { type, ascending } = e.detail;
const sortedList = genericSort(renderData.tests, type, ascending);
renderData.tests = sortedList;
initRender();
});
const doFilter = (type, apply) => {
const currentFilter = [
...new Set(JSON.parse(localStorage.getItem('filter'))),
];
if (apply) {
currentFilter.push(type);
} else {
const index = currentFilter.indexOf(type);
currentFilter.splice(index, 1);
}
localStorage.setItem('filter', JSON.stringify(currentFilter));
const filteredSubset = [];
if (currentFilter.length) {
currentFilter.forEach((filter) => {
filteredSubset.push(
...jsonData.tests.filter(({ outcome }) => outcome === filter)
);
});
renderData.tests = filteredSubset;
} else {
renderData.tests = jsonData.tests;
}
initRender();
};

View File

@ -0,0 +1,100 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Test Report</title>
<link href="style.css" rel="stylesheet" type="text/css"/>
</head>
<body onLoad="init()">
<h1 id="title"></h1>
<p>Report generated on 17-Dec-2020 at 14:02:36 by <a href="https://pypi.python.org/pypi/pytest-html">pytest-html</a> v1.22.0</p>
<h2>Environment</h2>
<table id="environment"><table>
<!-- TEMPLATES -->
<template id="template_environment_row">
<tr>
<td></td>
<td></td>
</tr>
</template>
<template id="template_a">
<a target="_blank"></a>
</template>
<template id="template_img">
<a target="_blank">
<img />
</a>
</template>
<template id="template_results-table__tbody">
<tbody class="results-table-row">
<tr>
<td class="col-result"></td>
<td class="col-name"></td>
<td class="col-duration"></td>
<td class="col-links"></td>
</tr>
<tr class="extras-row">
<td class="extra" colspan="4">
<div class="image"></div>
<div class="log"></div>
</td>
</tr>
</tbody>
</template>
<template id="template_results-table__head">
<thead id="results-table-head">
<tr>
<th class="sortable" data-column-type="outcome">Result</th>
<th class="sortable" data-column-type="nodeid">Test</th>
<th class="sortable" data-column-type="duration">Duration</th>
<th>Links</th>
</tr>
</thead>
</template>
<template id="template_results-table__head--empty">
<tr id="not-found-message">
<th colspan="4">No results found. Try to check the filters</th>
</tr>
</template>
<!-- END TEMPLATES -->
<div class="summary">
<div class="summary__data">
<h2>Summary</h2>
<p class="run-count"></p>
<p class="filter">(Un)check the boxes to filter the results.</p>
<input checked="true" class="filter" data-test-result="error" name="filter_checkbox" type="checkbox"/><span class="error"></span>
<input checked="true" class="filter" data-test-result="failed" name="filter_checkbox" type="checkbox"/><span class="failed"></span>
<input checked="true" class="filter" data-test-result="rerun" name="filter_checkbox" type="checkbox"/><span class="rerun"></span>
<input checked="true" class="filter" data-test-result="xfailed" name="filter_checkbox" type="checkbox"/><span class="xfailed"></span>
<input checked="true" class="filter" data-test-result="xpassed" name="filter_checkbox" type="checkbox"/><span class="xpassed"></span>
<input checked="true" class="filter" data-test-result="passed" name="filter_checkbox" type="checkbox"/><span class="passed"></span>
<input checked="true" class="filter" data-test-result="skipped" name="filter_checkbox" type="checkbox"/><span class="skipped"></span>
</div>
<div class="summary__reload">
<div class="summary__reload__button" onclick="location.reload()">
<div>There are still tests running. <br />Reload this page to ge the latest results!</div>
</div>
</div>
<div class="summary__spacer">
</div>
</div>
<h2>Results</h2>
<table id="results-table">
</table>
</body>
<footer>
<script src="events.js"></script>
<script src="dom.js"></script>
<script src="nextgendata.js"></script>
<script src="sort.js"></script>
<script src="index.js"></script>
<script src="main.js"></script>
</footer>
</html>

View File

@ -0,0 +1,134 @@
function htmlToElements(html) {
let temp = document.createElement('template');
temp.innerHTML = html;
return temp.content.childNodes;
}
const find = (selector, elem) => {
if (!elem) {
elem = document;
}
return elem.querySelector(selector);
};
const findAll = (selector, elem) => {
if (!elem) {
elem = document;
}
return toArray(elem.querySelectorAll(selector));
};
const templateEnvRow = find('#template_environment_row');
const templateResult = find('#template_results-table__tbody');
const aTag = find('#template_a');
const aTagImg = find('#template_img');
const listHeader = find('#template_results-table__head');
const listHeaderEmpty = find('#template_results-table__head--empty');
const removeChildren = (node) => {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
};
const getOutcome = ({ nodeid }, tests) => {
const relatedOutcome = tests
.filter((test) => test.nodeid === nodeid)
.map(({ outcome }) => outcome);
if (relatedOutcome.includes('failed')) {
return 'failed';
} else if (relatedOutcome.includes('error')) {
return 'error';
} else if (relatedOutcome.includes('xpassed')) {
return 'xpassed';
} else if (relatedOutcome.includes('xfailed')) {
return 'xfailed';
} else if (relatedOutcome.includes('skipped')) {
return 'skipped';
} else {
return 'passed';
}
};
const renderStatic = (obj) => {
find('#title').innerText = obj.title;
const rows = Object.keys(obj.environment).map((key) =>
dom.getStaticRow(key, obj.environment[key])
);
const table = find('#environment');
removeChildren(table);
rows.forEach((row) => table.appendChild(row));
};
const renderContent = ({ tests }) => {
const renderSet = tests.filter(({ when }) => when === 'call');
const rows = renderSet.map((test) =>
dom.getResultTBody(test, getOutcome(test, tests))
);
const table = find('#results-table');
removeChildren(table);
const tableHeader = dom.getListHeader();
if (!rows.length) {
tableHeader.appendChild(dom.getListHeaderEmpty());
}
table.appendChild(tableHeader);
rows.forEach((row) => !!row && table.appendChild(row));
};
const renderDerrived = ({ tests, collectedItems }) => {
const renderSet = tests.filter(({ when }) => when === 'call');
const possibleOutcomes = [
'passed',
'skipped',
'failed',
'error',
'xfailed',
'xpassed',
'rerun',
];
possibleOutcomes.forEach((outcome) => {
const count = renderSet.filter((test) => test.outcome === outcome).length;
find(`.${outcome}`).innerText = `${count} ${outcome}`;
if (!count) {
find(`input[data-test-result="${outcome}"]`).disabled = true;
}
});
let accTime = 0;
if (collectedItems === renderSet.length) {
tests.forEach(({ duration }) => (accTime += duration));
accTime = accTime.toFixed(2);
find(
'.run-count'
).innerText = `${renderSet.length} tests ran in ${accTime} seconds.`;
find('.summary__reload__button').classList.add('hidden');
} else {
find(
'.run-count'
).innerText = `${renderSet.length} / ${collectedItems} tests done`;
}
};
const bindEvents = () => {
findAll('.sortable').forEach((elem) => {
elem.addEventListener('click', (evt) => {
const { target: element } = evt;
const { columnType } = element.dataset;
doSort(columnType);
});
});
};
const renderPage = (subset, full) => {
renderStatic(jsonData);
renderContent(renderData);
renderDerrived(jsonData);
};
let renderData = { ...jsonData };
const initRender = () => {
setTimeout(() => {
renderPage(renderData);
bindEvents();
}, 0);
};

View File

@ -2,235 +2,114 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
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);
console.log(outcome);
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;
}
function toArray(iter) {
if (iter === null) {
return null;
}
return Array.prototype.slice.call(iter);
if (iter === null) {
return null;
}
return Array.prototype.slice.call(iter);
}
function find(selector, elem) { // eslint-disable-line no-redeclare
if (!elem) {
elem = document;
}
return elem.querySelector(selector);
function showAllExtras() {
// eslint-disable-line no-unused-vars
findAll('.col-result').forEach(showExtras);
}
function findAll(selector, elem) {
if (!elem) {
elem = document;
}
return toArray(elem.querySelectorAll(selector));
}
function sortColumn(elem) {
toggleSortStates(elem);
const colIndex = toArray(elem.parentNode.childNodes).indexOf(elem);
let key;
if (elem.classList.contains('result')) {
key = keyResult;
} else if (elem.classList.contains('links')) {
key = keyLink;
} else {
key = keyAlpha;
}
sortTable(elem, key(colIndex));
}
function showAllExtras() { // eslint-disable-line no-unused-vars
findAll('.col-result').forEach(showExtras);
}
function hideAllExtras() { // eslint-disable-line no-unused-vars
findAll('.col-result').forEach(hideExtras);
function hideAllExtras() {
// eslint-disable-line no-unused-vars
findAll('.col-result').forEach(hideExtras);
}
function showExtras(colresultElem) {
const extras = colresultElem.parentNode.nextElementSibling;
const expandcollapse = colresultElem.firstElementChild;
extras.classList.remove('collapsed');
expandcollapse.classList.remove('expander');
expandcollapse.classList.add('collapser');
const extras = colresultElem.parentNode.nextElementSibling;
const expandcollapse = colresultElem.firstElementChild;
extras.classList.remove('collapsed');
expandcollapse.classList.remove('expander');
expandcollapse.classList.add('collapser');
}
function hideExtras(colresultElem) {
const extras = colresultElem.parentNode.nextElementSibling;
const expandcollapse = colresultElem.firstElementChild;
extras.classList.add('collapsed');
expandcollapse.classList.remove('collapser');
expandcollapse.classList.add('expander');
}
function showFilters() {
const filterItems = document.getElementsByClassName('filter');
for (let i = 0; i < filterItems.length; i++)
filterItems[i].hidden = false;
const extras = colresultElem.parentNode.nextElementSibling;
const expandcollapse = colresultElem.firstElementChild;
extras.classList.add('collapsed');
expandcollapse.classList.remove('collapser');
expandcollapse.classList.add('expander');
}
function addCollapse() {
// Add links for show/hide all
const resulttable = find('table#results-table');
const showhideall = document.createElement('p');
showhideall.innerHTML = '<a href="javascript:showAllExtras()">Show all details</a> / ' +
'<a href="javascript:hideAllExtras()">Hide all details</a>';
resulttable.parentElement.insertBefore(showhideall, resulttable);
// Add links for show/hide all
const resulttable = find('table#results-table');
const showhideall = document.createElement('p');
showhideall.innerHTML =
'<a href="javascript:showAllExtras()">Show all details</a> / ' +
'<a href="javascript:hideAllExtras()">Hide all details</a>';
resulttable.parentElement.insertBefore(showhideall, resulttable);
// Add show/hide link to each result
findAll('.col-result').forEach(function(elem) {
const collapsed = getQueryParameter('collapsed') || 'Passed';
const extras = elem.parentNode.nextElementSibling;
const expandcollapse = document.createElement('span');
if (extras.classList.contains('collapsed')) {
expandcollapse.classList.add('expander');
} else if (collapsed.includes(elem.innerHTML)) {
extras.classList.add('collapsed');
expandcollapse.classList.add('expander');
} else {
expandcollapse.classList.add('collapser');
}
elem.appendChild(expandcollapse);
// Add show/hide link to each result
findAll('.col-result').forEach(function (elem) {
const collapsed = getQueryParameter('collapsed') || 'Passed';
const extras = elem.parentNode.nextElementSibling;
const expandcollapse = document.createElement('span');
if (extras.classList.contains('collapsed')) {
expandcollapse.classList.add('expander');
} else 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')) {
showExtras(event.currentTarget);
} else {
hideExtras(event.currentTarget);
}
});
elem.addEventListener('click', function (event) {
if (
event.currentTarget.parentNode.nextElementSibling.classList.contains(
'collapsed'
)
) {
showExtras(event.currentTarget);
} else {
hideExtras(event.currentTarget);
}
});
});
}
function getQueryParameter(name) {
const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
function init() {
// eslint-disable-line no-unused-vars
setTimeout(() => {
findAll('input[name="filter_checkbox"]').forEach((elem) => {
elem.addEventListener('click', (evt) => {
const { target: element } = evt;
const { testResult } = element.dataset;
function init () { // eslint-disable-line no-unused-vars
resetSortHeaders();
doFilter(testResult, element.checked);
});
});
initRender();
addCollapse();
showFilters();
sortColumn(find('.initial-sort'));
findAll('.sortable').forEach(function(elem) {
elem.addEventListener('click',
function() {
sortColumn(elem);
}, false);
});
}
function sortTable(clicked, keyFunc) {
const rows = findAll('.results-table-row');
const reversed = !clicked.classList.contains('asc');
const sortedRows = sort(rows, keyFunc, reversed);
/* Whole table is removed here because browsers acts much slower
* when appending existing elements.
*/
const thead = document.getElementById('results-table-head');
document.getElementById('results-table').remove();
const parent = document.createElement('table');
parent.id = 'results-table';
parent.appendChild(thead);
sortedRows.forEach(function(elem) {
parent.appendChild(elem);
});
document.getElementsByTagName('BODY')[0].appendChild(parent);
}
function sort(items, keyFunc, reversed) {
const sortArray = items.map(function(item, i) {
return [keyFunc(item), i];
});
sortArray.sort(function(a, b) {
const keyA = a[0];
const keyB = b[0];
if (keyA == keyB) return 0;
if (reversed) {
return keyA < keyB ? 1 : -1;
} else {
return keyA > keyB ? 1 : -1;
}
});
return sortArray.map(function(item) {
const index = item[1];
return items[index];
});
}
function keyAlpha(colIndex) {
return function(elem) {
return elem.childNodes[1].childNodes[colIndex].firstChild.data.toLowerCase();
};
}
function keyLink(colIndex) {
return function(elem) {
const dataCell = elem.childNodes[1].childNodes[colIndex].firstChild;
return dataCell == null ? '' : dataCell.innerText.toLowerCase();
};
}
function keyResult(colIndex) {
return function(elem) {
const strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed',
'Skipped', 'Passed'];
return strings.indexOf(elem.childNodes[1].childNodes[colIndex].firstChild.data);
};
}
function resetSortHeaders() {
findAll('.sort-icon').forEach(function(elem) {
elem.parentNode.removeChild(elem);
});
findAll('.sortable').forEach(function(elem) {
const 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 toggleSortStates(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')) {
resetSortHeaders();
elem.classList.remove('inactive');
elem.classList.add('active');
}
}
function isAllRowsHidden(value) {
return value.hidden == false;
}
function filterTable(elem) { // eslint-disable-line no-unused-vars
const outcomeAtt = 'data-test-result';
const outcome = elem.getAttribute(outcomeAtt);
const classOutcome = outcome + ' results-table-row';
const outcomeRows = document.getElementsByClassName(classOutcome);
for(let i = 0; i < outcomeRows.length; i++){
outcomeRows[i].hidden = !elem.checked;
}
const rows = findAll('.results-table-row').filter(isAllRowsHidden);
const allRowsHidden = rows.length == 0 ? true : false;
const notFoundMessage = document.getElementById('not-found-message');
notFoundMessage.hidden = !allRowsHidden;
});
}

View File

@ -0,0 +1,734 @@
const jsonData = {
title: 'Next Gen Report',
collectedItems: 10,
environment: {
Python: '3.7.7',
Platform: 'Darwin-19.6.0-x86_64-i386-64bit',
Packages: { pytest: '6.1.2', py: '1.9.0', pluggy: '0.13.1' },
Plugins: { metadata: '1.11.0', reportlog: '0.1.2', html: '2.1.2.dev78' },
},
tests: [
{
nodeid: 'test_html.py::test_url',
location: ['test_html.py', 3, 'test_url'],
keywords: { test_url: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.0001576979999999839,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url',
location: ['test_html.py', 3, 'test_url'],
keywords: { test_url: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'failed',
extras: [
{
name: 'Snapshot',
format_type: 'image',
content: 'snapshot_of_what_went_wrong.png',
mime_type: 'image/png',
extension: '.png',
},
{
name: 'Google',
format_type: 'url',
content: 'https://search.yahoo.com/',
mime_type: null,
extension: null,
},
],
longrepr: {
reprcrash: {
path: '/Users/jimbrannlund/dev/pytest-dev/testing-html/test_html.py',
lineno: 12,
message: 'assert False',
},
reprtraceback: {
reprentries: [
{
type: 'ReprEntry',
data: {
lines: [
'universe = <pytest_setup.database.TestDataCollection object at 0x7fe2cf1c4dd8>',
'driver = <selenium.webdriver.firefox.webdriver.WebDriver (session="2c586c44-7cb6-8e48-86fd-ae2d2982a3bd")>',
'',
'@pytest.mark.user(name="Bob", account=\'Rolfs Account\')',
"@pytest.mark.setup_data({'EnterpriseProject': [{'name': 'Dependencies', 'user': 'Bob'}]},",
"{'Activity': [",
"{'name': 'Move',",
"'project': 'Dependencies',",
"'user': 'Bob',",
"'dates': {'start_date': 1, 'end_date': 7}},",
"{'name': 'Nope',",
"'project': 'Dependencies',",
"'user': 'Bob',",
"'dates': {'start_date': 8, 'end_date': 14}}]})",
'def test_dependency_forces_date_change(universe, driver):',
'from test_automation.tools.date_handling import convert_to_correct_date',
' ',
'_to_depend_on = "Move"',
"_expected_dates = {'start_date': convert_to_correct_date(15), 'end_date': convert_to_correct_date(21)}",
' ',
"_user = universe.get('User', 'Bob')",
"_act = universe.get('Activity', 'Move')",
' ',
'ActivityDetailsPane(driver).',
"go_to_page(universe.get('User', 'Bob'),",
"universe.get('EnterpriseProject', 'Dependencies'),",
'universe.get("Activity", "Nope")). ',
"open_activity('Move'). ",
"> open_activity('Nope'). ",
'open_widgets_section(). ',
"get_widget('planletdependencies').",
'add_predecessor(_to_depend_on).',
"get_details_pane('activity').",
'close_details_pane().',
'click_planlet_checkbox(_act.id).',
"get_widget('date').",
'verify_dates(_expected_dates)',
'',
'test_automation/tests/ui_tests/plan_tests/activity_details_pane_test.py:121: ',
'_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ',
'test_automation/page_objects/details_panes/activity_details_pane.py:241: in open_widgets_section',
'_wait_for_plus_sign()',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/basepage/decorators.py:112: in wait_handler',
'value = func(*args, **kwargs)',
'test_automation/page_objects/details_panes/activity_details_pane.py:236: in _wait_for_plus_sign',
'if self.get_present_element(self._plus_sign_locator):',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/basepage/base_page.py:422: in get_present_element',
'return self._get(locator, expected_condition, params, timeout, error_msg, parent)',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/basepage/base_page.py:569: in _get',
'return WebDriverWait(_driver, timeout).until(exp_cond, error_msg)',
'_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ',
'',
'self = <selenium.webdriver.support.wait.WebDriverWait (session="2c586c44-7cb6-8e48-86fd-ae2d2982a3bd")>',
'method = <selenium.webdriver.support.expected_conditions.presence_of_element_located object at 0x7fe2cf1a7240>',
'message = "Element was never present!\nLocator of type <CSS_SELECTOR> with selector <.pp-rounded-btn__icon.category-bck-3> with ...nExpected condition: <class \'selenium.webdriver.support.expected_conditions.presence_of_element_located\'>\nTimeout: 30"',
'',
"def until(self, method, message=''):",
'"""Calls the method provided with the driver as an argument until the ',
'return value is not False."""',
'screen = None',
'stacktrace = None',
' ',
'end_time = time.time() + self._timeout',
'while True:',
'try:',
'value = method(self._driver)',
'if value:',
'return value',
'except self._ignored_exceptions as exc:',
"screen = getattr(exc, 'screen', None)",
"stacktrace = getattr(exc, 'stacktrace', None)",
'time.sleep(self._poll)',
'if time.time() > end_time:',
'break',
'> raise TimeoutException(message, screen, stacktrace)',
'E selenium.common.exceptions.TimeoutException: Message: Element was never present!',
'E Locator of type <CSS_SELECTOR> with selector <.pp-rounded-btn__icon.category-bck-3> with params <None>',
"E Expected condition: <class 'selenium.webdriver.support.expected_conditions.presence_of_element_located'>",
'E Timeout: 30',
'',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/selenium/webdriver/support/wait.py:80: TimeoutException',
'------------------------------- pytest-selenium --------------------------------',
'Driver log: /private/var/folders/nt/zg2hyyxj6k77sf_qk34xtqf40000gn/T/pytest-of-vgustafsson/pytest-12/test_dependency_forces_date_ch0/driver.log',
'URL: https://local.rnd.projectplace.com/#project/79788/plan/28838',
'WARNING: Failed to gather log types: Message: HTTP method not allowed',
'',
'----------------------------- Additional Reporting -----------------------------',
'Window size: width <1680>, height <983>',
"WARNING: Failed to get HAR: 'NoneType' object has no attribute 'har'",
'Logs can be found here: http://elk.rnd.projectplace.com/goto/152df50b1a44ad0',
],
reprfuncargs: { args: [] },
reprlocals: null,
reprfileloc: {
path: 'test_html.py',
lineno: 12,
message: 'AssertionError',
},
style: 'long',
},
},
],
extraline: null,
style: 'long',
},
sections: [],
chain: [
[
{
reprentries: [
{
type: 'ReprEntry',
data: {
lines: [
' def test_url():',
' """',
' bla bla bla bla',
' alb alb alb',
' @param: hello',
' :param just',
' """',
' # driver.get("https://www.google.com")',
'> assert False',
'E assert False',
],
reprfuncargs: { args: [] },
reprlocals: null,
reprfileloc: {
path: 'test_html.py',
lineno: 12,
message: 'AssertionError',
},
style: 'long',
},
},
],
extraline: null,
style: 'long',
},
{
path:
'/Users/jimbrannlund/dev/pytest-dev/testing-html/test_html.py',
lineno: 12,
message: 'assert False',
},
null,
],
],
},
when: 'call',
user_properties: [],
sections: [],
duration: 0.00020797699999997032,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url',
location: ['test_html.py', 3, 'test_url'],
keywords: { test_url: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 0.0001561270000000059,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url2',
location: ['test_html.py', 14, 'test_url2'],
keywords: { test_url2: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url2',
location: ['test_html.py', 14, 'test_url2'],
keywords: { test_url2: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url2',
location: ['test_html.py', 14, 'test_url2'],
keywords: { test_url2: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url4',
location: ['test_html.py', 14, 'test_url4'],
keywords: { test_url4: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url4',
location: ['test_html.py', 14, 'test_url4'],
keywords: { test_url4: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url4',
location: ['test_html.py', 14, 'test_url4'],
keywords: { test_url4: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url5',
location: ['test_html.py', 14, 'test_url5'],
keywords: { test_url5: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url5',
location: ['test_html.py', 14, 'test_url5'],
keywords: { test_url5: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url5',
location: ['test_html.py', 14, 'test_url5'],
keywords: { test_url5: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url6',
location: ['test_html.py', 14, 'test_url6'],
keywords: { test_url6: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url6',
location: ['test_html.py', 14, 'test_url6'],
keywords: { test_url6: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url6',
location: ['test_html.py', 14, 'test_url6'],
keywords: { test_url6: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url7',
location: ['test_html.py', 14, 'test_url7'],
keywords: { test_url7: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url7',
location: ['test_html.py', 14, 'test_url7'],
keywords: { test_url7: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url7',
location: ['test_html.py', 14, 'test_url7'],
keywords: { test_url7: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url8',
location: ['test_html.py', 14, 'test_url8'],
keywords: { test_url8: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url8',
location: ['test_html.py', 14, 'test_url8'],
keywords: { test_url8: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url8',
location: ['test_html.py', 14, 'test_url8'],
keywords: { test_url8: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url9',
location: ['test_html.py', 14, 'test_url9'],
keywords: { test_url9: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url9',
location: ['test_html.py', 14, 'test_url9'],
keywords: { test_url9: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url9',
location: ['test_html.py', 14, 'test_url9'],
keywords: { test_url9: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url10',
location: ['test_html.py', 14, 'test_url10'],
keywords: { test_url10: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.00010436599999996687,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url10',
location: ['test_html.py', 14, 'test_url10'],
keywords: { test_url10: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'call',
user_properties: [],
sections: [],
duration: 0.00011543000000002746,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url10',
location: ['test_html.py', 14, 'test_url10'],
keywords: { test_url10: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 9.126600000003426,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url112',
location: ['test_html.py', 3, 'test_url112'],
keywords: { test_url112: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'setup',
user_properties: [],
sections: [],
duration: 0.0001576979999999839,
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url112',
location: ['test_html.py', 3, 'test_url112'],
keywords: { test_url112: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'failed',
extras: [
{
name: 'Snapshot',
format_type: 'image',
content: 'snapshot_of_what_went_wrong.png',
mime_type: 'image/png',
extension: '.png',
},
{
name: 'Google',
format_type: 'url',
content: 'https://search.yahoo.com/',
mime_type: null,
extension: null,
},
],
longrepr: {
reprcrash: {
path: '/Users/jimbrannlund/dev/pytest-dev/testing-html/test_html.py',
lineno: 12,
message: 'assert False',
},
reprtraceback: {
reprentries: [
{
type: 'ReprEntry',
data: {
lines: [
'universe = <pytest_setup.database.TestDataCollection object at 0x7fe2cf1c4dd8>',
'driver = <selenium.webdriver.firefox.webdriver.WebDriver (session="2c586c44-7cb6-8e48-86fd-ae2d2982a3bd")>',
'',
'@pytest.mark.user(name="Bob", account=\'Rolfs Account\')',
"@pytest.mark.setup_data({'EnterpriseProject': [{'name': 'Dependencies', 'user': 'Bob'}]},",
"{'Activity': [",
"{'name': 'Move',",
"'project': 'Dependencies',",
"'user': 'Bob',",
"'dates': {'start_date': 1, 'end_date': 7}},",
"{'name': 'Nope',",
"'project': 'Dependencies',",
"'user': 'Bob',",
"'dates': {'start_date': 8, 'end_date': 14}}]})",
'def test_dependency_forces_date_change(universe, driver):',
'from test_automation.tools.date_handling import convert_to_correct_date',
' ',
'_to_depend_on = "Move"',
"_expected_dates = {'start_date': convert_to_correct_date(15), 'end_date': convert_to_correct_date(21)}",
' ',
"_user = universe.get('User', 'Bob')",
"_act = universe.get('Activity', 'Move')",
' ',
'ActivityDetailsPane(driver).',
"go_to_page(universe.get('User', 'Bob'),",
"universe.get('EnterpriseProject', 'Dependencies'),",
'universe.get("Activity", "Nope")). ',
"open_activity('Move'). ",
"> open_activity('Nope'). ",
'open_widgets_section(). ',
"get_widget('planletdependencies').",
'add_predecessor(_to_depend_on).',
"get_details_pane('activity').",
'close_details_pane().',
'click_planlet_checkbox(_act.id).',
"get_widget('date').",
'verify_dates(_expected_dates)',
'',
'test_automation/tests/ui_tests/plan_tests/activity_details_pane_test.py:121: ',
'_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ',
'test_automation/page_objects/details_panes/activity_details_pane.py:241: in open_widgets_section',
'_wait_for_plus_sign()',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/basepage/decorators.py:112: in wait_handler',
'value = func(*args, **kwargs)',
'test_automation/page_objects/details_panes/activity_details_pane.py:236: in _wait_for_plus_sign',
'if self.get_present_element(self._plus_sign_locator):',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/basepage/base_page.py:422: in get_present_element',
'return self._get(locator, expected_condition, params, timeout, error_msg, parent)',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/basepage/base_page.py:569: in _get',
'return WebDriverWait(_driver, timeout).until(exp_cond, error_msg)',
'_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ',
'',
'self = <selenium.webdriver.support.wait.WebDriverWait (session="2c586c44-7cb6-8e48-86fd-ae2d2982a3bd")>',
'method = <selenium.webdriver.support.expected_conditions.presence_of_element_located object at 0x7fe2cf1a7240>',
'message = "Element was never present!\nLocator of type <CSS_SELECTOR> with selector <.pp-rounded-btn__icon.category-bck-3> with ...nExpected condition: <class \'selenium.webdriver.support.expected_conditions.presence_of_element_located\'>\nTimeout: 30"',
'',
"def until(self, method, message=''):",
'"""Calls the method provided with the driver as an argument until the ',
'return value is not False."""',
'screen = None',
'stacktrace = None',
' ',
'end_time = time.time() + self._timeout',
'while True:',
'try:',
'value = method(self._driver)',
'if value:',
'return value',
'except self._ignored_exceptions as exc:',
"screen = getattr(exc, 'screen', None)",
"stacktrace = getattr(exc, 'stacktrace', None)",
'time.sleep(self._poll)',
'if time.time() > end_time:',
'break',
'> raise TimeoutException(message, screen, stacktrace)',
'E selenium.common.exceptions.TimeoutException: Message: Element was never present!',
'E Locator of type <CSS_SELECTOR> with selector <.pp-rounded-btn__icon.category-bck-3> with params <None>',
"E Expected condition: <class 'selenium.webdriver.support.expected_conditions.presence_of_element_located'>",
'E Timeout: 30',
'',
'/Users/vgustafsson/.virtualenvs/pytest/lib/python3.7/site-packages/selenium/webdriver/support/wait.py:80: TimeoutException',
'------------------------------- pytest-selenium --------------------------------',
'Driver log: /private/var/folders/nt/zg2hyyxj6k77sf_qk34xtqf40000gn/T/pytest-of-vgustafsson/pytest-12/test_dependency_forces_date_ch0/driver.log',
'URL: https://local.rnd.projectplace.com/#project/79788/plan/28838',
'WARNING: Failed to gather log types: Message: HTTP method not allowed',
'',
'----------------------------- Additional Reporting -----------------------------',
'Window size: width <1680>, height <983>',
"WARNING: Failed to get HAR: 'NoneType' object has no attribute 'har'",
'Logs can be found here: http://elk.rnd.projectplace.com/goto/152df50b1a44ad0',
],
reprfuncargs: { args: [] },
reprlocals: null,
reprfileloc: {
path: 'test_html.py',
lineno: 12,
message: 'AssertionError',
},
style: 'long',
},
},
],
extraline: null,
style: 'long',
},
sections: [],
chain: [
[
{
reprentries: [
{
type: 'ReprEntry',
data: {
lines: [
' def test_url112():',
' """',
' bla bla bla bla',
' alb alb alb',
' @param: hello',
' :param just',
' """',
' # driver.get("https://www.google.com")',
'> assert False',
'E assert False',
],
reprfuncargs: { args: [] },
reprlocals: null,
reprfileloc: {
path: 'test_html.py',
lineno: 12,
message: 'AssertionError',
},
style: 'long',
},
},
],
extraline: null,
style: 'long',
},
{
path:
'/Users/jimbrannlund/dev/pytest-dev/testing-html/test_html.py',
lineno: 12,
message: 'assert False',
},
null,
],
],
},
when: 'call',
user_properties: [],
sections: [],
duration: 0.00020797699999997032,
extra: [],
$report_type: 'TestReport',
},
{
nodeid: 'test_html.py::test_url112',
location: ['test_html.py', 3, 'test_url112'],
keywords: { test_url112: 1, 'testing-html': 1, 'test_html.py': 1 },
outcome: 'passed',
longrepr: null,
when: 'teardown',
user_properties: [],
sections: [],
duration: 0.0001561270000000059,
$report_type: 'TestReport',
},
],
};

View File

@ -0,0 +1,10 @@
const genericSort = (list, key, ascending) => {
const sorted = list.sort((a, b) =>
a[key] === b[key] ? 0 : a[key] === b[key] ? 1 : -1
);
if (ascending) {
sorted.reverse();
}
return sorted;
};

View File

@ -33,11 +33,16 @@ table {
******************************/
#environment td {
padding: 5px;
border: 1px solid #E6E6E6;
border: 1px solid #e6e6e6;
vertical-align: top;
}
#environment tr:nth-child(odd) {
background-color: #f6f6f6;
}
#environment ul {
margin: 0;
padding: 0 20px;
}
/******************************
* TEST RESULT COLORS
@ -65,6 +70,14 @@ span.xpassed,
color: red;
}
.col-result {
text-transform: capitalize;
}
.col-links__extra {
margin-right: 3px;
}
/******************************
* RESULTS TABLE
*
@ -85,7 +98,7 @@ span.xpassed,
#results-table th,
#results-table td {
padding: 5px;
border: 1px solid #E6E6E6;
border: 1px solid #e6e6e6;
text-align: left;
}
#results-table th {
@ -142,14 +155,14 @@ div.video video {
.expander::after {
content: " (show details)";
color: #BBB;
color: #bbb;
font-style: italic;
cursor: pointer;
}
.collapser::after {
content: " (hide details)";
color: #BBB;
color: #bbb;
font-style: italic;
cursor: pointer;
}
@ -160,27 +173,61 @@ div.video video {
.sortable {
cursor: pointer;
}
.sortable.asc:after {
content: " ";
position: relative;
left: 5px;
bottom: -12.5px;
border: 10px solid #4caf50;
border-bottom: 0;
border-left-color: transparent;
border-right-color: transparent;
}
.sortable.desc:after {
content: " ";
position: relative;
left: 5px;
bottom: 12.5px;
border: 10px solid #4caf50;
border-top: 0;
border-left-color: transparent;
border-right-color: transparent;
}
.sort-icon {
font-size: 0px;
float: left;
margin-right: 5px;
margin-top: 5px;
/*triangle*/
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
.hidden, .summary__reload__button.hidden {
display: none;
}
.inactive .sort-icon {
/*finish triangle*/
border-top: 8px solid #E6E6E6;
.summary {
display: flex;
}
.asc.active .sort-icon {
/*finish triangle*/
border-bottom: 8px solid #999;
.summary__data {
flex: 0 0 550px;
}
.desc.active .sort-icon {
/*finish triangle*/
border-top: 8px solid #999;
.summary__reload {
flex: 1 1;
display: flex;
justify-content: center;
}
.summary__reload__button {
flex: 0 0 300px;
display: flex;
color: white;
font-weight: bold;
background-color: #4caf50;
text-align: center;
justify-content: center;
align-items: center;
border-radius: 3px;
cursor: pointer;
}
.summary__reload__button:hover {
background-color: #46a049;
}
.summary__spacer {
flex: 0 0 550px;
}
input.filter {
margin-left: 10px;
}