Fix: Initial sort and query param (#637)

This commit is contained in:
Jim Brännlund 2023-04-08 18:18:28 +02:00 committed by GitHub
parent 89746e3203
commit f8758f98c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 17 deletions

View File

@ -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. 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. Note that the query parameter takes precedence.
.. code-block:: ini .. code-block:: ini
@ -262,8 +262,8 @@ Note that the query parameter takes precedence.
[pytest] [pytest]
render_collapsed = failed,error 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 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 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. 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:`passed`
* :code:`skipped` * :code:`skipped`
@ -282,9 +282,29 @@ The following query parameters may be passed:
* :code:`xpassed` * :code:`xpassed`
* :code:`rerun` * :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 .. _@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/ .. _ansi2html: https://pypi.python.org/pypi/ansi2html/
.. _Content Security Policy (CSP): https://developer.mozilla.org/docs/Web/Security/CSP/ .. _Content Security Policy (CSP): https://developer.mozilla.org/docs/Web/Security/CSP/
.. _JSON: https://json.org/ .. _JSON: https://json.org/
.. _pytest-metadata: https://pypi.python.org/pypi/pytest-metadata/ .. _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 .. _time.strftime: https://docs.python.org/3/library/time.html#time.strftime

View File

@ -1,8 +1,22 @@
const { manager } = require('./datamanager.js') const { manager } = require('./datamanager.js')
const storageModule = require('./storage.js') const storageModule = require('./storage.js')
const genericSort = (list, key, ascending) => { const genericSort = (list, key, ascending, customOrder) => {
const sorted = list.sort((a, b) => a[key] === b[key] ? 0 : a[key] > b[key] ? 1 : -1) 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) { if (ascending) {
sorted.reverse() sorted.reverse()
@ -14,8 +28,14 @@ const doInitSort = () => {
const type = storageModule.getSort() const type = storageModule.getSort()
const ascending = storageModule.getSortDirection() const ascending = storageModule.getSortDirection()
const list = manager.testSubset const list = manager.testSubset
const sortedList = genericSort(list, type, ascending) const initialOrder = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed', 'Skipped', 'Passed']
manager.setRender(sortedList) console.log(list)
if (type?.toLowerCase() === 'original') {
manager.setRender(list)
} else {
const sortedList = genericSort(list, type, ascending, initialOrder)
manager.setRender(sortedList)
}
} }
const doSort = (type) => { const doSort = (type) => {

View File

@ -65,17 +65,17 @@ const getCollapsedCategory = (config) => {
const collapsedItems = new URLSearchParams(url.search).get('collapsed') const collapsedItems = new URLSearchParams(url.search).get('collapsed')
switch (true) { switch (true) {
case !config && collapsedItems === null: case !config && collapsedItems === null:
categories = ['passed']; categories = ['passed']
break; break
case collapsedItems?.length === 0 || /^["']{2}$/.test(collapsedItems): case collapsedItems?.length === 0 || /^["']{2}$/.test(collapsedItems):
categories = []; categories = []
break; break
case /^all$/.test(collapsedItems) || (collapsedItems === null && /^all$/.test(config)): case /^all$/.test(collapsedItems) || (collapsedItems === null && /^all$/.test(config)):
categories = [...possibleFilters]; categories = [...possibleFilters]
break; break
default: default:
categories = collapsedItems?.split(',').map(item => item.toLowerCase()) || config; categories = collapsedItems?.split(',').map(item => item.toLowerCase()) || config
break; break
} }
} else { } else {
categories = [] categories = []

View File

@ -102,7 +102,7 @@ describe('Sort tests', () => {
doInitSort() doInitSort()
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([ 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', () => { it('has stored sort preference', () => {
@ -116,6 +116,17 @@ describe('Sort tests', () => {
'failed', 'passed', 'passed', 'passed', 'passed', 'passed', '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', () => { describe('doSort', () => {
let getSortMock let getSortMock