diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 38a53000..e9bffa9a 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -29,7 +29,7 @@ jobs: run: python -m tox -e docs tests: - uses: ./.github/workflows/tests.yml + uses: pytest-dev/pytest-html/.github/workflows/tests.yml@master publish: name: Publish to PyPI registry diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 252aa36c..d97d3156 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -6,5 +6,4 @@ on: jobs: tests: - if: github.repository_owner == 'pytest-dev' - uses: ./.github/workflows/tests.yml + uses: pytest-dev/pytest-html/.github/workflows/tests.yml@master diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c914d2fe..42afa78d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,9 +8,20 @@ jobs: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: - fail-fast: false matrix: include: + - os: ubuntu-latest + name: py36-ubuntu + python-version: 3.6 + + - os: windows-latest + name: py36-windows + python-version: 3.6 + + - os: macOS-latest + name: py36-mac + python-version: 3.6 + - os: ubuntu-latest name: py37-ubuntu python-version: 3.7 @@ -47,31 +58,18 @@ jobs: name: py39-mac python-version: 3.9 - - os: ubuntu-latest - name: py310-ubuntu - python-version: '3.10' - - - os: windows-latest - name: py310-windows - python-version: '3.10' - - - os: macOS-latest - name: py310-mac - python-version: '3.10' - - os: ubuntu-latest name: pypy3-ubuntu - python-version: pypy-3.8 + python-version: pypy3 - # TODO: This test takes 10(!) times as long as the regular py38 on Windows -# - os: windows-latest -# name: pypy3-windows -# python-version: pypy-3.8 + - os: windows-latest + name: pypy3-windows + python-version: pypy3 # https://github.com/pytest-dev/pytest-html/issues/482 - - os: macOS-latest - name: pypy3-mac - python-version: pypy-3.8 +# - os: macOS-latest +# name: pypy3-mac +# python-version: pypy3 - os: ubuntu-latest name: devel-ubuntu @@ -80,37 +78,21 @@ jobs: steps: - name: Set Newline Behavior run : git config --global core.autocrlf false - - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - + - uses: actions/checkout@master - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v2 with: python-version: ${{ matrix['python-version'] }} - - - name: Upgrade pip - run: python -m pip install --upgrade pip - - name: Install tox run: python -m pip install --upgrade tox - - name: Get Tox Environment Name From Matrix Name uses: rishabhgupta/split-by@v1 id: split-matrix-name with: string: '${{ matrix.name }}' split-by: '-' - - - name: Test with coverage - if: "! contains(matrix.name, 'pypy3')" - run: python -m tox -rvv -e ${{ steps.split-matrix-name.outputs._0}}-cov - - - name: Test without coverage - if: "contains(matrix.name, 'pypy3')" - run: python -m tox -e ${{ steps.split-matrix-name.outputs._0}} - + - name: Test with tox + run: python -m tox -e ${{ steps.split-matrix-name.outputs._0}}-cov # TODO: https://github.com/pytest-dev/pytest-html/issues/481 # - name: Upload coverage to codecov # if: github.event.schedule == '' @@ -126,9 +108,9 @@ jobs: name: grunt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v1 with: node-version: '12.x' - name: Install Dependencies diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d72169dc..aa5c6711 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,13 +37,13 @@ repos: hooks: - id: pyupgrade args: [--py3-plus] - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v7.13.0 - hooks: - - id: eslint - additional_dependencies: - - eslint@7.13.0 - args: [src] +# - repo: https://github.com/pre-commit/mirrors-eslint +# rev: v7.13.0 +# hooks: +# - id: eslint +# additional_dependencies: +# - eslint@7.13.0 +# args: [src] - repo: local hooks: - id: rst diff --git a/pyproject.toml b/pyproject.toml index d1b48e16..fd6ab5f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,4 +8,3 @@ build-backend = "setuptools.build_meta" [tool.setuptools_scm] local_scheme = "no-local-version" write_to = "src/pytest_html/__version.py" - diff --git a/src/pytest_html/html_report.py b/src/pytest_html/html_report.py index 66e10f07..28afe823 100644 --- a/src/pytest_html/html_report.py +++ b/src/pytest_html/html_report.py @@ -178,7 +178,7 @@ def _generate_report(self, session): ] with open( - os.path.join(os.path.dirname(__file__), "resources", "main.js") + os.path.join(os.path.dirname(__file__), "resources", "old_main.js") ) as main_js_fp: main_js = main_js_fp.read() diff --git a/src/pytest_html/plugin.py b/src/pytest_html/plugin.py index 9f567e15..e039bca9 100644 --- a/src/pytest_html/plugin.py +++ b/src/pytest_html/plugin.py @@ -4,12 +4,12 @@ import os import pytest +from _pytest.pathlib import Path from . import extras # noqa: F401 from .html_report import HTMLReport from .nextgen import NextGenReport -from _pytest.pathlib import Path def pytest_addhooks(pluginmanager): from . import hooks diff --git a/src/pytest_html/resources/index.html b/src/pytest_html/resources/index.html index 417809e0..f75cf531 100644 --- a/src/pytest_html/resources/index.html +++ b/src/pytest_html/resources/index.html @@ -64,17 +64,17 @@

Environment

Summary

(Un)check the boxes to filter the results.

- - + + - - + + - +
@@ -86,7 +86,7 @@

Summary

Results

- +
- \ No newline at end of file + diff --git a/src/pytest_html/resources/main.js b/src/pytest_html/resources/main.js index 9419fe76..63c26498 100644 --- a/src/pytest_html/resources/main.js +++ b/src/pytest_html/resources/main.js @@ -61,8 +61,8 @@ function addCollapse() { const resulttable = find('table#results-table'); const showhideall = document.createElement('p'); showhideall.innerHTML = - 'Show all details / ' + - 'Hide all details'; + 'Show all details / ' + + 'Hide all details'; resulttable.parentElement.insertBefore(showhideall, resulttable); // Add show/hide link to each result @@ -82,9 +82,9 @@ function addCollapse() { elem.addEventListener('click', function (event) { if ( - event.currentTarget.parentNode.nextElementSibling.classList.contains( - 'collapsed' - ) + event.currentTarget.parentNode.nextElementSibling.classList.contains( + 'collapsed' + ) ) { showExtras(event.currentTarget); } else { diff --git a/src/pytest_html/resources/old_main.js b/src/pytest_html/resources/old_main.js new file mode 100644 index 00000000..f26513c9 --- /dev/null +++ b/src/pytest_html/resources/old_main.js @@ -0,0 +1,246 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 toArray(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 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 showExtras(colresultElem) { + 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() { + let visibleString = getQueryParameter('visible') || 'all'; + visibleString = visibleString.toLowerCase(); + const checkedItems = visibleString.split(','); + + const filterItems = document.getElementsByClassName('filter'); + for (let i = 0; i < filterItems.length; i++) { + filterItems[i].hidden = false; + + if (visibleString != 'all') { + filterItems[i].checked = checkedItems.includes(filterItems[i].getAttribute('data-test-result')); + filterTable(filterItems[i]); + } + } +} + +function addCollapse() { + // Add links for show/hide all + const resulttable = find('table#results-table'); + const showhideall = document.createElement('p'); + showhideall.innerHTML = 'Show all details / ' + + 'Hide all details'; + 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); + + 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, ' ')); +} + +function init () { // eslint-disable-line no-unused-vars + resetSortHeaders(); + + 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; +} diff --git a/testing/js_test_report.html b/testing/js_test_report.html index 5b4fae82..205fab29 100644 --- a/testing/js_test_report.html +++ b/testing/js_test_report.html @@ -12,7 +12,7 @@ - +
diff --git a/testing/test_pytest_html.py b/testing/test_pytest_html.py index ac9a1500..a3c90805 100644 --- a/testing/test_pytest_html.py +++ b/testing/test_pytest_html.py @@ -347,7 +347,7 @@ def test_resources(self, testdir): assert result.ret == 0 content = pkg_resources.resource_string( - "pytest_html", os.path.join("resources", "main.js") + "pytest_html", os.path.join("resources", "old_main.js") ) content = content.decode("utf-8") assert content