diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 6d14b3a0..c60ed9e7 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -254,7 +254,7 @@ By setting the query parameter to empty string :code:`?collapsed=""` **none** of Note that the query parameter is case insensitive, so passing :code:`PASSED` and :code:`passed` has the same effect. -You can also set the collapsed behaviour by setting the :code:`render_collapsed` in a configuration file (pytest.ini, setup.cfg, etc). +You can also set the collapsed behaviour by setting :code:`render_collapsed` in a configuration file (pytest.ini, setup.cfg, etc). Note that the query parameter takes precedence. .. code-block:: ini @@ -262,8 +262,8 @@ Note that the query parameter takes precedence. [pytest] render_collapsed = failed,error -Controlling Test Result Visibility Via Query Params -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Controlling Test Result Visibility +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, all tests are visible, regardless of their results. It is possible to control which tests are visible on page load by passing the :code:`visible` query parameter. To use this parameter, please pass a comma separated list @@ -272,7 +272,7 @@ tests in the report that have outcome :code:`passed` or :code:`skipped`. Note that this match is case insensitive, so passing :code:`PASSED` and :code:`passed` has the same effect. -The following query parameters may be passed: +The following values may be passed: * :code:`passed` * :code:`skipped` @@ -282,9 +282,29 @@ The following query parameters may be passed: * :code:`xpassed` * :code:`rerun` +Results Table Sorting +~~~~~~~~~~~~~~~~~~~~~ + +You can change the sort order of the results table on page load by passing the :code:`sort` query parameter. + +The following values may be passed: + +* :code:`result` +* :code:`testId` +* :code:`duration` +* :code:`original` + +Note that the values are case *sensitive*. + +``original`` means that a best effort is made to sort the table in the order of execution. +If tests are run in parallel (with `pytest-xdist`_ for example), then the order may not be +in the correct order. + + .. _@pytest.hookimpl(tryfirst=True): https://docs.pytest.org/en/stable/writing_plugins.html#hook-function-ordering-call-example .. _ansi2html: https://pypi.python.org/pypi/ansi2html/ .. _Content Security Policy (CSP): https://developer.mozilla.org/docs/Web/Security/CSP/ .. _JSON: https://json.org/ .. _pytest-metadata: https://pypi.python.org/pypi/pytest-metadata/ +.. _pytest-xdist: https://pypi.python.org/pypi/pytest-xdist/ .. _time.strftime: https://docs.python.org/3/library/time.html#time.strftime diff --git a/src/pytest_html/scripts/sort.js b/src/pytest_html/scripts/sort.js index aee74719..b3c63ed7 100644 --- a/src/pytest_html/scripts/sort.js +++ b/src/pytest_html/scripts/sort.js @@ -1,8 +1,22 @@ const { manager } = require('./datamanager.js') const storageModule = require('./storage.js') -const genericSort = (list, key, ascending) => { - const sorted = list.sort((a, b) => a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1) +const genericSort = (list, key, ascending, customOrder) => { + let sorted + if (customOrder) { + sorted = list.sort((a, b) => { + const aValue = a.result.toLowerCase() + const bValue = b.result.toLowerCase() + + const aIndex = customOrder.findIndex(item => item.toLowerCase() === aValue) + const bIndex = customOrder.findIndex(item => item.toLowerCase() === bValue) + + // Compare the indices to determine the sort order + return aIndex - bIndex + }) + } else { + sorted = list.sort((a, b) => a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1) + } if (ascending) { sorted.reverse() @@ -14,8 +28,14 @@ const doInitSort = () => { const type = storageModule.getSort() const ascending = storageModule.getSortDirection() const list = manager.testSubset - const sortedList = genericSort(list, type, ascending) - manager.setRender(sortedList) + const initialOrder = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed', 'Skipped', 'Passed'] + console.log(list) + if (type?.toLowerCase() === 'original') { + manager.setRender(list) + } else { + const sortedList = genericSort(list, type, ascending, initialOrder) + manager.setRender(sortedList) + } } const doSort = (type) => { diff --git a/src/pytest_html/scripts/storage.js b/src/pytest_html/scripts/storage.js index 5af8171a..c634aa43 100644 --- a/src/pytest_html/scripts/storage.js +++ b/src/pytest_html/scripts/storage.js @@ -65,17 +65,17 @@ const getCollapsedCategory = (config) => { const collapsedItems = new URLSearchParams(url.search).get('collapsed') switch (true) { case !config && collapsedItems === null: - categories = ['passed']; - break; + categories = ['passed'] + break case collapsedItems?.length === 0 || /^["']{2}$/.test(collapsedItems): - categories = []; - break; + categories = [] + break case /^all$/.test(collapsedItems) || (collapsedItems === null && /^all$/.test(config)): - categories = [...possibleFilters]; - break; + categories = [...possibleFilters] + break default: - categories = collapsedItems?.split(',').map(item => item.toLowerCase()) || config; - break; + categories = collapsedItems?.split(',').map(item => item.toLowerCase()) || config + break } } else { categories = [] diff --git a/testing/unittest.js b/testing/unittest.js index 17d6543d..5df90c03 100644 --- a/testing/unittest.js +++ b/testing/unittest.js @@ -102,7 +102,7 @@ describe('Sort tests', () => { doInitSort() expect(managerSpy.callCount).to.eql(1) expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([ - 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', + 'failed', 'passed', 'passed', 'passed', 'passed', 'passed', ]) }) it('has stored sort preference', () => { @@ -116,6 +116,17 @@ describe('Sort tests', () => { 'failed', 'passed', 'passed', 'passed', 'passed', 'passed', ]) }) + it('keeps original test execution order', () => { + sortMock = sinon.stub(storageModule, 'getSort').returns('original') + sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(false) + managerSpy = sinon.spy(dataModule.manager, 'setRender') + + doInitSort() + expect(managerSpy.callCount).to.eql(1) + expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([ + 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', + ]) + }) }) describe('doSort', () => { let getSortMock