refactor: stop overwriting pytest data (#597)

This commit is contained in:
Jim Brännlund 2023-03-18 22:52:01 +01:00 committed by GitHub
parent 0408b0d504
commit 6eff86d537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 60 deletions

View File

@ -61,6 +61,9 @@ class BaseReport:
def data(self): def data(self):
return self._data return self._data
def add_test(self, test):
self._data["tests"].append(test)
def set_data(self, key, value): def set_data(self, key, value):
self._data[key] = value self._data[key] = value
@ -206,7 +209,7 @@ class BaseReport:
def pytest_sessionstart(self, session): def pytest_sessionstart(self, session):
config = session.config config = session.config
if hasattr(config, "_metadata") and config._metadata: if hasattr(config, "_metadata") and config._metadata:
self._report.data["environment"] = self._generate_environment() self._report.set_data("environment", self._generate_environment())
session.config.hook.pytest_html_report_title(report=self._report) session.config.hook.pytest_html_report_title(report=self._report)
@ -214,7 +217,7 @@ class BaseReport:
session.config.hook.pytest_html_results_table_header(cells=header_cells) session.config.hook.pytest_html_results_table_header(cells=header_cells)
self._report.set_data("resultsTableHeader", header_cells.html) self._report.set_data("resultsTableHeader", header_cells.html)
self._report.data["runningState"] = "Started" self._report.set_data("runningState", "Started")
self._generate_report() self._generate_report()
@pytest.hookimpl(trylast=True) @pytest.hookimpl(trylast=True)
@ -224,7 +227,7 @@ class BaseReport:
summary=self._report.data["additionalSummary"]["summary"], summary=self._report.data["additionalSummary"]["summary"],
postfix=self._report.data["additionalSummary"]["postfix"], postfix=self._report.data["additionalSummary"]["postfix"],
) )
self._report.data["runningState"] = "Finished" self._report.set_data("runningState", "Finished")
self._generate_report() self._generate_report()
@pytest.hookimpl(trylast=True) @pytest.hookimpl(trylast=True)
@ -235,35 +238,36 @@ class BaseReport:
@pytest.hookimpl(trylast=True) @pytest.hookimpl(trylast=True)
def pytest_collection_finish(self, session): def pytest_collection_finish(self, session):
self._report.data["collectedItems"] = len(session.items) self._report.set_data("collectedItems", len(session.items))
@pytest.hookimpl(trylast=True) @pytest.hookimpl(trylast=True)
def pytest_runtest_logreport(self, report): def pytest_runtest_logreport(self, report):
data = self._config.hook.pytest_report_to_serializable( data = {
config=self._config, report=report "duration": report.duration,
) "when": report.when,
}
test_id = report.nodeid test_id = report.nodeid
if report.when != "call": if report.when != "call":
test_id += f"::{report.when}" test_id += f"::{report.when}"
data["nodeid"] = test_id data["testId"] = test_id
# Order here matters! # Order here matters!
log = report.longreprtext or report.capstdout or "No log output captured." log = report.longreprtext or report.capstdout or "No log output captured."
data["longreprtext"] = _handle_ansi(log) data["log"] = _handle_ansi(log)
data["outcome"] = _process_outcome(report) data["result"] = _process_outcome(report)
row_cells = self.Cells() row_cells = self.Cells()
self._config.hook.pytest_html_results_table_row(report=report, cells=row_cells) self._config.hook.pytest_html_results_table_row(report=report, cells=row_cells)
data.update({"resultsTableRow": row_cells.html}) data["resultsTableRow"] = row_cells.html
table_html = [] table_html = []
self._config.hook.pytest_html_results_table_html(report=report, data=table_html) self._config.hook.pytest_html_results_table_html(report=report, data=table_html)
data.update({"tableHtml": table_html}) data["tableHtml"] = table_html
data.update({"extras": self._process_extras(report, test_id)}) data["extras"] = self._process_extras(report, test_id)
self._report.data["tests"].append(data) self._report.add_test(data)
self._generate_report() self._generate_report()

View File

@ -60,8 +60,8 @@
<template id="template_results-table__head"> <template id="template_results-table__head">
<thead id="results-table-head"> <thead id="results-table-head">
<tr> <tr>
<th class="sortable" data-column-type="outcome">Result</th> <th class="sortable" data-column-type="result">Result</th>
<th class="sortable" data-column-type="nodeid">Test</th> <th class="sortable" data-column-type="testid">Test</th>
<th class="sortable" data-column-type="duration">Duration</th> <th class="sortable" data-column-type="duration">Duration</th>
<th>Links</th> <th>Links</th>
</tr> </tr>

View File

@ -6,7 +6,7 @@ class DataManager {
const dataBlob = { ...data, tests: data.tests.map((test, index) => ({ const dataBlob = { ...data, tests: data.tests.map((test, index) => ({
...test, ...test,
id: `test_${index}`, id: `test_${index}`,
collapsed: collapsedCategories.includes(test.outcome.toLowerCase()), collapsed: collapsedCategories.includes(test.result.toLowerCase()),
})) } })) }
this.data = { ...dataBlob } this.data = { ...dataBlob }
this.renderData = { ...dataBlob } this.renderData = { ...dataBlob }

View File

@ -52,7 +52,7 @@ const dom = {
const header = listHeader.content.cloneNode(true) const header = listHeader.content.cloneNode(true)
const sortAttr = storageModule.getSort() const sortAttr = storageModule.getSort()
const sortAsc = JSON.parse(storageModule.getSortDirection()) const sortAsc = JSON.parse(storageModule.getSortDirection())
const sortables = ['outcome', 'nodeid', 'duration'] const sortables = ['result', 'testId', 'duration']
sortables.forEach((sortCol) => { sortables.forEach((sortCol) => {
if (sortCol === sortAttr) { if (sortCol === sortAttr) {
@ -67,23 +67,23 @@ const dom = {
}, },
getListHeaderEmpty: () => listHeaderEmpty.content.cloneNode(true), getListHeaderEmpty: () => listHeaderEmpty.content.cloneNode(true),
getColGroup: () => templateCollGroup.content.cloneNode(true), getColGroup: () => templateCollGroup.content.cloneNode(true),
getResultTBody: ({ nodeid, id, longreprtext, duration, extras, resultsTableRow, tableHtml, outcome, collapsed }) => { getResultTBody: ({ testId, id, log, duration, extras, resultsTableRow, tableHtml, result, collapsed }) => {
const outcomeLower = outcome.toLowerCase() const resultLower = result.toLowerCase()
let formattedDuration = formatDuration(duration) let formattedDuration = formatDuration(duration)
formattedDuration = formatDuration < 1 ? formattedDuration.ms : formattedDuration.formatted formattedDuration = formatDuration < 1 ? formattedDuration.ms : formattedDuration.formatted
const resultBody = templateResult.content.cloneNode(true) const resultBody = templateResult.content.cloneNode(true)
resultBody.querySelector('tbody').classList.add(outcomeLower) resultBody.querySelector('tbody').classList.add(resultLower)
resultBody.querySelector('.col-result').innerText = outcome resultBody.querySelector('.col-result').innerText = result
resultBody.querySelector('.col-result').classList.add(`${collapsed ? 'expander' : 'collapser'}`) resultBody.querySelector('.col-result').classList.add(`${collapsed ? 'expander' : 'collapser'}`)
resultBody.querySelector('.col-result').dataset.id = id resultBody.querySelector('.col-result').dataset.id = id
resultBody.querySelector('.col-name').innerText = nodeid resultBody.querySelector('.col-name').innerText = testId
resultBody.querySelector('.col-duration').innerText = duration < 1 ? formatDuration(duration).ms : formatDuration(duration).formatted resultBody.querySelector('.col-duration').innerText = duration < 1 ? formatDuration(duration).ms : formatDuration(duration).formatted
if (longreprtext) { if (log) {
// resultBody.querySelector('.log').innerText = longreprtext // resultBody.querySelector('.log').innerText = log
resultBody.querySelector('.log').innerHTML = longreprtext resultBody.querySelector('.log').innerHTML = log
} }
// if (collapsed || !longreprtext) { // if (collapsed || !longreprtext) {
if (collapsed) { if (collapsed) {

View File

@ -2,7 +2,7 @@ const { manager } = require('./datamanager.js')
const storageModule = require('./storage.js') const storageModule = require('./storage.js')
const getFilteredSubSet = (filter) => const getFilteredSubSet = (filter) =>
manager.allData.tests.filter(({ outcome }) => filter.includes(outcome.toLowerCase())) manager.allData.tests.filter(({ result }) => filter.includes(result.toLowerCase()))
const doInitFilter = () => { const doInitFilter = () => {
const currentFilter = storageModule.getVisible() const currentFilter = storageModule.getVisible()

View File

@ -29,7 +29,7 @@ const renderStatic = () => {
} }
const renderContent = (tests) => { const renderContent = (tests) => {
const renderSet = tests.filter(({ when, outcome }) => when === 'call' || outcome === 'Error' ) const renderSet = tests.filter(({ when, result }) => when === 'call' || result === 'Error' )
const rows = renderSet.map(dom.getResultTBody) const rows = renderSet.map(dom.getResultTBody)
const table = document.querySelector('#results-table') const table = document.querySelector('#results-table')
removeChildren(table) removeChildren(table)
@ -62,30 +62,30 @@ const renderContent = (tests) => {
} }
const renderDerived = (tests, collectedItems, isFinished) => { const renderDerived = (tests, collectedItems, isFinished) => {
const renderSet = tests.filter(({ when, outcome }) => when === 'call' || outcome === 'Error') const renderSet = tests.filter(({ when, result }) => when === 'call' || result === 'Error')
const possibleOutcomes = [ const possibleResults = [
{ outcome: 'passed', label: 'Passed' }, { result: 'passed', label: 'Passed' },
{ outcome: 'skipped', label: 'Skipped' }, { result: 'skipped', label: 'Skipped' },
{ outcome: 'failed', label: 'Failed' }, { result: 'failed', label: 'Failed' },
{ outcome: 'error', label: 'Errors' }, { result: 'error', label: 'Errors' },
{ outcome: 'xfailed', label: 'Unexpected failures' }, { result: 'xfailed', label: 'Unexpected failures' },
{ outcome: 'xpassed', label: 'Unexpected passes' }, { result: 'xpassed', label: 'Unexpected passes' },
{ outcome: 'rerun', label: 'Reruns' }, { result: 'rerun', label: 'Reruns' },
] ]
const currentFilter = getVisible() const currentFilter = getVisible()
possibleOutcomes.forEach(({ outcome, label }) => { possibleResults.forEach(({ result, label }) => {
const count = renderSet.filter((test) => test.outcome.toLowerCase() === outcome).length const count = renderSet.filter((test) => test.result.toLowerCase() === result).length
const input = document.querySelector(`input[data-test-result="${outcome}"]`) const input = document.querySelector(`input[data-test-result="${result}"]`)
document.querySelector(`.${outcome}`).innerText = `${count} ${label}` document.querySelector(`.${result}`).innerText = `${count} ${label}`
input.disabled = !count input.disabled = !count
input.checked = currentFilter.includes(outcome) input.checked = currentFilter.includes(result)
}) })
const numberOfTests = renderSet.filter(({ outcome }) => const numberOfTests = renderSet.filter(({ result }) =>
['Passed', 'Failed', 'XPassed', 'XFailed'].includes(outcome)).length ['Passed', 'Failed', 'XPassed', 'XFailed'].includes(result)).length
if (isFinished) { if (isFinished) {
const accTime = tests.reduce((prev, { duration }) => prev + duration, 0) const accTime = tests.reduce((prev, { duration }) => prev + duration, 0)

View File

@ -41,7 +41,7 @@ const setFilter = (currentFilter) => {
const getSort = () => { const getSort = () => {
const url = new URL(window.location.href) const url = new URL(window.location.href)
return new URLSearchParams(url.search).get('sort') || 'outcome' return new URLSearchParams(url.search).get('sort') || 'result'
} }
const setSort = (type) => { const setSort = (type) => {
const url = new URL(window.location.href) const url = new URL(window.location.href)

View File

@ -13,27 +13,27 @@ const setTestData = () => {
[ [
{ {
'id': 'passed_1', 'id': 'passed_1',
'outcome': 'passed', 'result': 'passed',
}, },
{ {
'id': 'failed_2', 'id': 'failed_2',
'outcome': 'failed', 'result': 'failed',
}, },
{ {
'id': 'passed_3', 'id': 'passed_3',
'outcome': 'passed', 'result': 'passed',
}, },
{ {
'id': 'passed_4', 'id': 'passed_4',
'outcome': 'passed', 'result': 'passed',
}, },
{ {
'id': 'passed_5', 'id': 'passed_5',
'outcome': 'passed', 'result': 'passed',
}, },
{ {
'id': 'passed_6', 'id': 'passed_6',
'outcome': 'passed', 'result': 'passed',
}, },
], ],
} }
@ -55,7 +55,7 @@ describe('Filter tests', () => {
doInitFilter() doInitFilter()
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([]) expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([])
}) })
it('exclude passed', () => { it('exclude passed', () => {
getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['failed']) getFilterMock = sinon.stub(storageModule, 'getVisible').returns(['failed'])
@ -63,7 +63,7 @@ describe('Filter tests', () => {
doInitFilter() doInitFilter()
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql(['failed']) expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql(['failed'])
}) })
}) })
describe('doFilter', () => { describe('doFilter', () => {
@ -76,7 +76,7 @@ describe('Filter tests', () => {
doFilter('passed', true) doFilter('passed', true)
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([
'passed', 'passed', 'passed', 'passed', 'passed', 'passed', 'passed', 'passed', 'passed', 'passed',
]) ])
}) })
@ -101,18 +101,18 @@ describe('Sort tests', () => {
doInitSort() doInitSort()
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([
'passed', 'failed', 'passed', 'passed', 'passed', 'passed', 'passed', 'failed', 'passed', 'passed', 'passed', 'passed',
]) ])
}) })
it('has stored sort preference', () => { it('has stored sort preference', () => {
sortMock = sinon.stub(storageModule, 'getSort').returns('outcome') sortMock = sinon.stub(storageModule, 'getSort').returns('result')
sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(false) sortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(false)
managerSpy = sinon.spy(dataModule.manager, 'setRender') managerSpy = sinon.spy(dataModule.manager, 'setRender')
doInitSort() doInitSort()
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([
'failed', 'passed', 'passed', 'passed', 'passed', 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', 'passed',
]) ])
}) })
@ -127,16 +127,16 @@ describe('Sort tests', () => {
afterEach(() => [ afterEach(() => [
getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy, getSortMock, setSortMock, getSortDirectionMock, setSortDirection, managerSpy,
].forEach((fn) => fn.restore())) ].forEach((fn) => fn.restore()))
it('sort on outcome', () => { it('sort on result', () => {
getSortMock = sinon.stub(storageModule, 'getSort').returns(null) getSortMock = sinon.stub(storageModule, 'getSort').returns(null)
setSortMock = sinon.stub(storageModule, 'setSort') setSortMock = sinon.stub(storageModule, 'setSort')
getSortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(null) getSortDirectionMock = sinon.stub(storageModule, 'getSortDirection').returns(null)
setSortDirection = sinon.stub(storageModule, 'setSortDirection') setSortDirection = sinon.stub(storageModule, 'setSortDirection')
managerSpy = sinon.spy(dataModule.manager, 'setRender') managerSpy = sinon.spy(dataModule.manager, 'setRender')
doSort('outcome') doSort('result')
expect(managerSpy.callCount).to.eql(1) expect(managerSpy.callCount).to.eql(1)
expect(dataModule.manager.testSubset.map(({ outcome }) => outcome)).to.eql([ expect(dataModule.manager.testSubset.map(({ result }) => result)).to.eql([
'passed', 'passed', 'passed', 'passed', 'passed', 'failed', 'passed', 'passed', 'passed', 'passed', 'passed', 'failed',
]) ])
}) })