Fix: Collapsed state between redraws (#703)

This commit is contained in:
Jim Brännlund 2023-07-28 17:28:14 +02:00 committed by GitHub
parent 8a4ece40a8
commit af4c6537dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 175 additions and 20 deletions

88
package-lock.json generated
View File

@ -10,6 +10,7 @@
"eslint": "^8.20.0",
"eslint-config-google": "^0.14.0",
"mocha": "^10.0.0",
"mock-local-storage": "^1.1.24",
"nyc": "^15.1.0",
"sass": "^1.52.3",
"sinon": "^14.0.0"
@ -1497,6 +1498,17 @@
"integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==",
"dev": true
},
"node_modules/core-js": {
"version": "3.32.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz",
"integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==",
"dev": true,
"hasInstallScript": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@ -1739,6 +1751,12 @@
"node": ">=6.0.0"
}
},
"node_modules/dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
"dev": true
},
"node_modules/domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -2300,6 +2318,16 @@
"node": ">=10.13.0"
}
},
"node_modules/global": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
"dev": true,
"dependencies": {
"min-document": "^2.19.0",
"process": "^0.11.10"
}
},
"node_modules/globals": {
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
@ -3145,6 +3173,15 @@
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
"dev": true
},
"node_modules/min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
"dev": true,
"dependencies": {
"dom-walk": "^0.1.0"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@ -3298,6 +3335,16 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/mock-local-storage": {
"version": "1.1.24",
"resolved": "https://registry.npmjs.org/mock-local-storage/-/mock-local-storage-1.1.24.tgz",
"integrity": "sha512-NEfmw+yEK9oe6xCfOnTaJ6Dz+L3eu6vsZopJlxflXYxr7Mg3EV+S0NXKUQlY9AAeDEdaPZDSUGq1Gi6kLSa5PA==",
"dev": true,
"dependencies": {
"core-js": "^3.30.2",
"global": "^4.3.2"
}
},
"node_modules/module-deps": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz",
@ -6172,6 +6219,12 @@
"integrity": "sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg==",
"dev": true
},
"core-js": {
"version": "3.32.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.0.tgz",
"integrity": "sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww==",
"dev": true
},
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@ -6371,6 +6424,12 @@
"esutils": "^2.0.2"
}
},
"dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
"dev": true
},
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -6791,6 +6850,16 @@
"is-glob": "^4.0.3"
}
},
"global": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
"dev": true,
"requires": {
"min-document": "^2.19.0",
"process": "^0.11.10"
}
},
"globals": {
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
@ -7425,6 +7494,15 @@
}
}
},
"min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
"dev": true,
"requires": {
"dom-walk": "^0.1.0"
}
},
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@ -7549,6 +7627,16 @@
}
}
},
"mock-local-storage": {
"version": "1.1.24",
"resolved": "https://registry.npmjs.org/mock-local-storage/-/mock-local-storage-1.1.24.tgz",
"integrity": "sha512-NEfmw+yEK9oe6xCfOnTaJ6Dz+L3eu6vsZopJlxflXYxr7Mg3EV+S0NXKUQlY9AAeDEdaPZDSUGq1Gi6kLSa5PA==",
"dev": true,
"requires": {
"core-js": "^3.30.2",
"global": "^4.3.2"
}
},
"module-deps": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/module-deps/-/module-deps-6.2.3.tgz",

View File

@ -4,7 +4,7 @@
"build:css": "sass --no-source-map --no-error-css src/layout/css/style.scss src/pytest_html/resources/style.css",
"build:jsapp": "browserify ./src/pytest_html/scripts/index.js > ./src/pytest_html/resources/app.js",
"lint": "eslint src/pytest_html/scripts/ testing/",
"unit": "nyc mocha testing/**/unittest.js",
"unit": "nyc mocha testing/**/unittest.js --require mock-local-storage",
"all": "npm run lint && npm run unit && npm run build:css && npm run build:jsapp"
},
"devDependencies": {
@ -13,6 +13,7 @@
"eslint": "^8.20.0",
"eslint-config-google": "^0.14.0",
"mocha": "^10.0.0",
"mock-local-storage": "^1.1.24",
"nyc": "^15.1.0",
"sass": "^1.52.3",
"sinon": "^14.0.0"

View File

@ -1,19 +1,25 @@
const { getCollapsedCategory } = require('./storage.js')
const { getCollapsedCategory, setCollapsedIds } = require('./storage.js')
class DataManager {
setManager(data) {
const collapsedCategories = [...getCollapsedCategory(data.renderCollapsed)]
const collapsedIds = []
const tests = Object.values(data.tests).flat().map((test, index) => {
const collapsed = collapsedCategories.includes(test.result.toLowerCase())
const id = `test_${index}`
if (collapsed) {
collapsedIds.push(id)
}
return {
...test,
id: `test_${index}`,
id,
collapsed,
}
})
const dataBlob = { ...data, tests }
this.data = { ...dataBlob }
this.renderData = { ...dataBlob }
setCollapsedIds(collapsedIds)
}
get allData() {
@ -47,6 +53,10 @@ class DataManager {
get environment() {
return this.renderData.environment
}
get initialSort() {
return this.data.initialSort
}
}
module.exports = {

View File

@ -1,4 +1,5 @@
const { manager } = require('./datamanager.js')
const { doSort } = require('./sort.js')
const storageModule = require('./storage.js')
const getFilteredSubSet = (filter) =>
@ -20,6 +21,9 @@ const doFilter = (type, show) => {
const currentFilter = storageModule.getVisible()
const filteredSubset = getFilteredSubSet(currentFilter)
manager.setRender(filteredSubset)
const sortColumn = storageModule.getSort()
doSort(sortColumn, true)
}
module.exports = {

View File

@ -2,7 +2,14 @@ const { dom, findAll } = require('./dom.js')
const { manager } = require('./datamanager.js')
const { doSort } = require('./sort.js')
const { doFilter } = require('./filter.js')
const { getVisible, getSort, getSortDirection, possibleFilters } = require('./storage.js')
const {
getVisible,
getCollapsedIds,
setCollapsedIds,
getSort,
getSortDirection,
possibleFilters,
} = require('./storage.js')
const removeChildren = (node) => {
while (node.firstChild) {
@ -22,7 +29,7 @@ const renderStatic = () => {
}
const renderContent = (tests) => {
const sortAttr = getSort(manager.allData.initialSort)
const sortAttr = getSort(manager.initialSort)
const sortAsc = JSON.parse(getSortDirection())
const rows = tests.map(dom.getResultTBody)
const table = document.getElementById('results-table')
@ -53,7 +60,17 @@ const renderContent = (tests) => {
findAll('.collapsible td:not(.col-links').forEach((elem) => {
elem.addEventListener('click', ({ target }) => {
manager.toggleCollapsedItem(target.parentElement.dataset.id)
const id = target.parentElement.dataset.id
manager.toggleCollapsedItem(id)
const collapsedIds = getCollapsedIds()
if (collapsedIds.includes(id)) {
const updated = collapsedIds.filter((item) => item !== id)
setCollapsedIds(updated)
} else {
collapsedIds.push(id)
setCollapsedIds(collapsedIds)
}
redraw()
})
})
@ -73,6 +90,14 @@ const bindEvents = () => {
const { testResult } = element.dataset
doFilter(testResult, element.checked)
const collapsedIds = getCollapsedIds()
const updated = manager.renderData.tests.map((test) => {
return {
...test,
collapsed: collapsedIds.includes(test.id),
}
})
manager.setRender(updated)
redraw()
}
@ -88,10 +113,13 @@ const bindEvents = () => {
})
document.getElementById('show_all_details').addEventListener('click', () => {
manager.allCollapsed = false
setCollapsedIds([])
redraw()
})
document.getElementById('hide_all_details').addEventListener('click', () => {
manager.allCollapsed = true
const allIds = manager.renderData.tests.map((test) => test.id)
setCollapsedIds(allIds)
redraw()
})
}

View File

@ -43,10 +43,14 @@ const durationSort = (list, ascending) => {
}
const doInitSort = () => {
const type = storageModule.getSort(manager.allData.initialSort)
const type = storageModule.getSort(manager.initialSort)
const ascending = storageModule.getSortDirection()
const list = manager.testSubset
const initialOrder = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed', 'Skipped', 'Passed']
storageModule.setSort(type)
storageModule.setSortDirection(ascending)
if (type?.toLowerCase() === 'original') {
manager.setRender(list)
} else {
@ -66,14 +70,19 @@ const doInitSort = () => {
}
}
const doSort = (type) => {
const newSortType = storageModule.getSort(manager.allData.initialSort) !== type
const doSort = (type, skipDirection) => {
const newSortType = storageModule.getSort(manager.initialSort) !== type
const currentAsc = storageModule.getSortDirection()
const ascending = newSortType ? true : !currentAsc
let ascending
if (skipDirection) {
ascending = currentAsc
} else {
ascending = newSortType ? false : !currentAsc
}
storageModule.setSort(type)
storageModule.setSortDirection(ascending)
const list = manager.testSubset
const list = manager.testSubset
const sortedList = type === 'duration' ? durationSort(list, ascending) : genericSort(list, type, ascending)
manager.setRender(sortedList)
}

View File

@ -30,7 +30,7 @@ const hideCategory = (categoryToHide) => {
const settings = [...new Set(currentVisible)].filter((f) => f !== categoryToHide).join(',')
url.searchParams.set('visible', settings)
history.pushState({}, null, unescape(url.href))
window.history.pushState({}, null, unescape(url.href))
}
const showCategory = (categoryToShow) => {
@ -44,7 +44,7 @@ const showCategory = (categoryToShow) => {
const noFilter = possibleFilters.length === settings.length || !settings.length
noFilter ? url.searchParams.delete('visible') : url.searchParams.set('visible', settings.join(','))
history.pushState({}, null, unescape(url.href))
window.history.pushState({}, null, unescape(url.href))
}
const getSort = (initialSort) => {
@ -59,7 +59,7 @@ const getSort = (initialSort) => {
const setSort = (type) => {
const url = new URL(window.location.href)
url.searchParams.set('sort', type)
history.pushState({}, null, unescape(url.href))
window.history.pushState({}, null, unescape(url.href))
}
const getCollapsedCategory = (renderCollapsed) => {
@ -87,17 +87,21 @@ const getCollapsedCategory = (renderCollapsed) => {
return categories
}
const getSortDirection = () => JSON.parse(sessionStorage.getItem('sortAsc'))
const getSortDirection = () => JSON.parse(sessionStorage.getItem('sortAsc')) || false
const setSortDirection = (ascending) => sessionStorage.setItem('sortAsc', ascending)
const getCollapsedIds = () => JSON.parse(sessionStorage.getItem('collapsedIds')) || []
const setCollapsedIds = (list) => sessionStorage.setItem('collapsedIds', JSON.stringify(list))
module.exports = {
getVisible,
hideCategory,
showCategory,
getCollapsedIds,
setCollapsedIds,
getSort,
getSortDirection,
setSort,
getSortDirection,
setSortDirection,
getCollapsedCategory,
possibleFilters,

View File

@ -44,6 +44,9 @@ const mockWindow = (queryParam) => {
location: {
href: `https://example.com/page?${queryParam}`,
},
history: {
pushState: sinon.stub(),
},
}
originalWindow = global.window
global.window = mock
@ -76,12 +79,17 @@ describe('Filter tests', () => {
})
})
describe('doFilter', () => {
let originalWindow
after(() => global.window = originalWindow)
it('removes all but passed', () => {
mockWindow()
getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['passed'])
managerSpy = sinon.spy(dataModule.manager, 'setRender')
doFilter('passed', true)
expect(managerSpy.callCount).to.eql(1)
expect(managerSpy.callCount).to.eql(2)
expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([
'passed', 'passed', 'passed', 'passed', 'passed',
])
@ -134,9 +142,12 @@ describe('Sort tests', () => {
let managerSpy
let sortMock
let sortDirectionMock
beforeEach(() => dataModule.manager.resetRender())
let originalWindow
before(() => mockWindow())
beforeEach(() => dataModule.manager.resetRender())
afterEach(() => [sortMock, sortDirectionMock, managerSpy].forEach((fn) => fn.restore()))
after(() => global.window = originalWindow)
it('has no stored sort', () => {
sortMock = sinon.stub(storageModule, 'getSort').returns('result')
sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(null)
@ -191,7 +202,7 @@ describe('Sort tests', () => {
doSort('result')
expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([
'passed', 'passed', 'passed', 'passed', 'passed', 'failed',
'failed', 'passed', 'passed', 'passed', 'passed', 'passed',
])
})
})