def generate_html(self): generated = datetime.datetime.now() with open(os.path.join(base_path, "main.js")) as main_f: doc = html.html( self.head, html.body( html.script(raw(main_f.read())), html.p( "Report generated on %s at %s" % (generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S")), html.h2("Summary"), html.p( "%i tests ran in %.1f seconds." % ( sum(self.test_count.itervalues()), (self.suite_times["end"] - self.suite_times["start"]) / 1000.0, ), html.br(), html.span("%i passed" % self.test_count["PASS"], class_="pass"), ", ", html.span("%i skipped" % self.test_count["SKIP"], class_="skip"), ", ", html.span("%i failed" % self.test_count["UNEXPECTED_FAIL"], class_="fail"), ", ", html.span("%i errors" % self.test_count["UNEXPECTED_ERROR"], class_="error"), ".", html.br(), html.span( "%i expected failures" % self.test_count["EXPECTED_FAIL"], class_="expected_fail" ), ", ", html.span( "%i unexpected passes" % self.test_count["UNEXPECTED_PASS"], class_="unexpected_pass" ), ".", ), html.h2("Results"), html.table( [ html.thead( html.tr( [ html.th("Result", class_="sortable", col="result"), html.th("Class", class_="sortable", col="class"), html.th("Test Name", class_="sortable", col="name"), html.th("Duration", class_="sortable numeric", col="duration"), html.th("Links"), ] ), id="results-table-head", ), html.tbody(self.result_rows, id="results-table-body"), ], id="results-table", ), ), ), ) return u"<!DOCTYPE html>\n" + doc.unicode(indent=2)
def pytest_html_results_table_header(cells): try: cells.insert(2, html.th('Description')) cells.insert(3, html.th('Time', class_='sortable time', col='time')) cells.pop() except: print("error occur, cannot update report")
def pytest_html_results_table_header(cells): cells.insert(0, html.th('Time', class_='sortable time', col='time')) cells.insert(1, html.th('Test', class_='sortable', col='shortname')) cells.insert(1, html.th('Module', class_='sortable', col='shortname')) cells.pop() cells.pop(-2) cells.insert(3, html.th('Input Data', class_='sortable', col='data'))
def pytest_html_results_table_header(cells): ''' 插入Description列和Test_nodeid列,删除原有的Test列 ''' cells.insert(1, html.th('Description')) cells.insert(2, html.th('Test_nodeid')) cells.pop(2)
def insert_data(self, tables_data: dict): if not isinstance(tables_data, dict): raise TypeError( f'{self.__module__} supports only dict as InputParam') self.tables_processed = len(tables_data.keys()) for raw_table_name, raw_table_data in tables_data.items(): csv_file_path = raw_table_data['Athena']['FilePath'] database_name, table_name = raw_table_name.split('.') self.data_results.append( html.div( html.h3(f"Input data: {raw_table_name}"), html.table( html.tr([ html.th('Database:', class_='database-name'), html.th(database_name) ]), html.tr([ html.th('Table name:', class_='table-name'), html.th(table_name) ]), self._generate_glue_report(raw_table_data['Glue']), self._generate_s3_report(raw_table_data['S3']), self._generate_athena_report(raw_table_data['Athena']), class_="processed-table"), html.div(f"{self._get_csv_data(csv_file_path)}", table_name=table_name, class_="data-tables")))
def pytest_html_results_table_header(cells): cells.insert(2, html.th('Description')) # 添加Description列 cells.insert(1, html.th('Test_nodeid')) # 添加Test_nodeid列, cells.insert(1, html.th('Time', class_='sortable time', col='time')) # 添加Time列 cells.pop(3) # 删除Test列 cells.pop() # 删除Link列
def pytest_html_results_table_header(cells): try: cells.insert(2, html.th('ModuleName')) # 模块名称 cells.insert(3, html.th('Description')) # 用例描述 cells.pop() except: pass
def pytest_html_results_table_header(cells): cells.insert(1, html.th('Description')) cells.insert(2, html.th('Test')) #增加一列,解决用例参数入参为中文时正常显示 cells.pop(3) #将原有的test删除 cells.insert(3, html.th('URL')) cells.insert(4, html.th('Assertions')) cells.insert(-2, html.th('Exec_Time', class_='sortable time', col='time')) cells.pop()
def pytest_html_results_table_header(cells): cells[1] = html.th('Method') cells.insert(2, html.th('Endpoint')) cells.insert(3, html.th('Parameters')) cells.insert(4, html.th('Body')) cells.insert(5, html.th('Restart')) # Remove links cells.pop()
def make_result_table(self): return html.table(html.thead( html.tr(html.th("Subsuite", class_='sortable', col='subsuite'), html.th("Subsuite Errors"), html.th("Test Executions"), html.th("Details"))), html.tbody(self.make_table_rows( self.subsuite_results), id='results-table-body'), id='results-table')
def make_regression_table(self): return html.table(html.thead(html.tr( html.th("Parent Test", col='parent'), html.th("Subtest", col='subtest'), html.th("Expected", col='expected'), html.th("Result", col='result'), ), id='results-table-head'), html.tbody(*self.make_table_rows(), id='results-table-body'), id='results-table')
def generate_html(self): generated = datetime.datetime.now() with open(os.path.join(base_path, "main.js")) as main_f: doc = html.html( self.head, html.body( html.script(raw(main_f.read()), ), html.p( 'Report generated on %s at %s' % (generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')), html.h2('Summary'), html.p( '%i tests ran in %.1f seconds.' % (sum(self.test_count.itervalues()), (self.suite_times["end"] - self.suite_times["start"]) / 1000.), html.br(), html.span('%i passed' % self.test_count["PASS"], class_='pass'), ', ', html.span('%i skipped' % self.test_count["SKIP"], class_='skip'), ', ', html.span('%i failed' % self.test_count["UNEXPECTED_FAIL"], class_='fail'), ', ', html.span('%i errors' % self.test_count["UNEXPECTED_ERROR"], class_='error'), '.', html.br(), html.span('%i expected failures' % self.test_count["EXPECTED_FAIL"], class_='expected_fail'), ', ', html.span('%i unexpected passes' % self.test_count["UNEXPECTED_PASS"], class_='unexpected_pass'), '.'), html.h2('Results'), html.table([ html.thead(html.tr([ html.th( 'Result', class_='sortable', col='result'), html.th( 'Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links') ]), id='results-table-head'), html.tbody(self.result_rows, id='results-table-body') ], id='results-table')))) return u"<!DOCTYPE html>\n" + doc.unicode(indent=2)
def make_regression_table(self): return html.table( html.thead( html.tr( html.th("Parent Test", col='parent'), html.th("Subtest", col='subtest'), html.th("Expected", col='expected'), html.th("Result", col='result'), ), id='results-table-head' ), html.tbody(*self.make_table_rows(), id='results-table-body'), id='results-table' )
def make_result_table(self): return html.table( html.thead( html.tr( html.th("Subsuite"), html.th("Subsuite Errors"), html.th("Test Regressions"), html.th("Details") ) ), html.tbody( self.make_table_rows(self.subsuite_results) ) )
def pytest_sessionfinish(self, session, exitstatus): self._make_report_dir() logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.link(rel='stylesheet', href='style.css'), html.script(src='jquery.js'), html.script(src='main.js')), html.body( html.p('Report generated on %s at %s ' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), )), html.div([html.p( html.span('%i tests' % numtests, class_='all clickable'), ' ran in %i seconds.' % suite_time_delta, html.br(), html.span('%i passed' % self.passed, class_='passed clickable'), ', ', html.span('%i skipped' % self.skipped, class_='skipped clickable'), ', ', html.span('%i failed' % self.failed, class_='failed clickable'), ', ', html.span('%i errors' % self.errors, class_='error clickable'), '.', html.br(), ), html.span('Hide all errors', class_='clickable hide_all_errors'), ', ', html.span('Show all errors', class_='clickable show_all_errors'), ], id='summary-wrapper'), html.div(id='summary-space'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), # html.th('Output')]), id='results-table-head'), html.th('Links')]), id='results-table-head'), html.tbody(*self.test_logs, id='results-table-body')], id='results-table'))) logfile.write( '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' + doc.unicode( indent=2)) logfile.close() self.process_screenshot_files() self.process_debug_event_files() self.process_performance_files()
def make_regression_table(self): return html.table( html.thead( html.tr( html.th("Parent Test"), html.th("Subtest"), html.th("Expected"), html.th("Result"), html.th("Message") ) ), html.tbody( *self.make_table_rows() ) )
def _generate_s3_report(s3_data: dict): s3_link = s3_data['S3Link'] s3_data.pop('S3Link') file_results = [ html.p(item) for item in s3_data['FileResults'] if not item.endswith('/') ] s3_report = html.tr([ html.th(f'S3 results:', class_='s3-results'), html.th( *file_results, html.p(html.a(s3_link, href=s3_link)), ) ]) return s3_report
def make_result_table(self): return html.table( html.thead( html.tr( html.th("Subsuite", class_='sortable', col='subsuite'), html.th("Subsuite Errors"), html.th("Test Executions"), html.th("Details") ) ), html.tbody( self.make_table_rows(self.subsuite_results),id='results-table-body' ), id='results-table' )
def _create_test_steps_table(self): # Create test steps table test_steps_table = html.table(class_="steps_table") test_steps_table.append(html.th("Action name", class_="step_name")) test_steps_table.append(html.th("User id", class_="step_user_id")) test_steps_table.append( html.th("Status code", class_="step_status_code")) test_steps_table.append( html.th("Request headers", class_="step_request_headers")) test_steps_tbody = html.tbody(class_="steps_table_tbody") test_steps_table.append(test_steps_tbody) return test_steps_table, test_steps_tbody
def pytest_html_results_table_row(report, cells): """ Inserts the contents for the 'description' column : the docstring of the test function """ node_bits = report.nodeid.split('::') file = node_bits[0] test_id = node_bits[1:] line = report.location[1] cells[1] = html.th(file) cells.insert(2, html.td('::'.join(test_id) if len(node_bits) > 1 else '')) cells.insert(3, html.td(report.description)) # cells.insert(1, html.td(datetime.utcnow(), class_='col-time')) # cells.pop() # use setuptools_scm to get git hash and build the codecov URL to go and see the source of the test # parsed_version = version_from_scm('.') # if parsed_version is not None: # hsh = parsed_version.node + ('' if not parsed_version.dirty else '-dirty') try: hsh = get_git_hash() cells[-1] = html.td( html.a('source', href='https://codecov.io/gh/smarie/python-valid8/src/' + hsh + '/' + file + '#L' + str(line))) except: cells[-1] = html.td('could not read git version')
def pytest_html_results_table_header(cells): """ 设置用例描述表头 :param cells: :return: """ cells.insert(2, html.th('Description')) cells.pop()
def pytest_html_results_table_header(cells): cells.pop() cells.insert(4, html.th('Title')) title = cells.pop() duration = cells.pop() test = cells.pop() cells.insert(1, title) cells.insert(2, test) cells.insert(3, duration)
def generate_html(self): generated = datetime.utcnow() with open(os.path.join(base_path, "main.js")) as main_f: doc = html.html( self.head, html.body( html.script(raw(main_f.read())), html.p('Report generated on %s at %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'))), html.h2('Environment'), html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(self.env.items()) if v], id='environment'), html.h2('Summary'), html.p('%i tests ran in %.1f seconds.' % (sum(self.test_count.itervalues()), (self.suite_times["end"] - self.suite_times["start"]) / 1000.), html.br(), html.span('%i passed' % self.test_count["PASS"], class_='pass'), ', ', html.span('%i skipped' % self.test_count["SKIP"], class_='skip'), ', ', html.span('%i failed' % self.test_count["UNEXPECTED_FAIL"], class_='fail'), ', ', html.span('%i errors' % self.test_count["UNEXPECTED_ERROR"], class_='error'), '.', html.br(), html.span('%i expected failures' % self.test_count["EXPECTED_FAIL"], class_='expected_fail'), ', ', html.span('%i unexpected passes' % self.test_count["UNEXPECTED_PASS"], class_='unexpected_pass'), '.'), html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Test', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(self.result_rows, id='results-table-body')], id='results-table'))) return u"<!DOCTYPE html>\n" + doc.unicode(indent=2)
def pytest_html_results_table_header(session, cells): if session.config.getoption("--batch-input-file", None): (reference, demo) = session.config.getoption("--batch-base-names").split(',') cells[0] = html.th( 'Result', class_='sortable result initial-sort asc active', title= f'Passed if the test results for this domain in both the {reference} and {demo} instances is 100%, Failed otherwise.' ) cells[1] = html.th('Domain', class_='sortable') cells.insert( 2, html. th('Delta Score (%)', class_='sortable numeric', title= f'The difference between the {reference} score and the {demo} score for this domain' )) cells.insert(3, html.th('Subtest Results (hover for details)')) cells.insert(4, html.th('New Failures', class_='sortable')) cells.insert(5, html.th('New Warnings', class_='sortable'))
def gen_html_table(mtx): from py.xml import html # print("DBG genhtmltable", mtx) result = html.table( html.thead(html.tr([html.th(x) for x in mtx[0]])), # header html.tbody(*[ html.tr([html.td(x) for x in row]) # rows for row in mtx[1:] ]), class_="fixed_headers") # make it fixed height scrollable # https://codepen.io/tjvantoll/pen/JEKIu # result = str(result) + """ # """ return str(result)
def html_summary_table(self): cells = [ html.th(header, class_=header_class) for header, header_class in zip( COLUMN_HEADERS, COLUMN_HEADER_CLASSES) ] self.config.hook.pytest_aggreport_html_table_header(cells=cells) tbody = [ html.tr(cells, id='aggregate-report-header'), ] for case_report in self.case_reports.values(): tbody.append(case_report.html_table_row) html_report = html.table(html.tbody(tbody), id='aggregate-report-table') return html_report
def gen_html_table( mtx ): from py.xml import html # print("DBG genhtmltable", mtx) result = html.table( html.thead( html.tr( [html.th( x ) for x in mtx[0] ] ) ), # header html.tbody( *[ html.tr( [html.td( x ) for x in row] ) # rows for row in mtx[1:] ] ), class_="fixed_headers" ) # make it fixed height scrollable # https://codepen.io/tjvantoll/pen/JEKIu # result = str(result) + """ # """ return str(result)
def pytest_html_results_table_header(cells): cells.insert(0, html.th('用例名称', style="width:30%;")) cells.insert(1, html.th('用例数据', style="width:36%;")) # cells.insert(2, html.th('执行时间', class_='sortable time', col='time')) cells.insert(2, html.th('编写人', style="width:5%;")) cells.insert(3, html.th('修改人', style="width:5%;")) cells.insert(4, html.th("开始时间")) cells.pop() change_opts = { "Test": { "text": "用例方法", "style": "width: 7%;" }, "Duration": { "text": "耗时(秒)", "style": "width:9%;" }, "Result": { "text": "测试结果", "style": "width:10%;" }, } for cell in cells: value = cell[0] if cell else "" if value in change_opts: details = change_opts[value] cell[0] = details["text"] skey = "style" if skey in details: add_style = details.get(skey, "") style = cell.attr.__dict__.get("style", "") if style: cell.attr.__dict__.update(dict(style="{};{}".format(style.rstrip(";"), add_style))) else: cell.attr.__dict__.update(dict(style=add_style))
def handle_dcmp(self, dcmp): self.div_container.append( html.h2('Diff between %s and %s' % (dcmp.left, dcmp.right))) if len(dcmp.left_only) == 0 and len(dcmp.right_only) == 0 and len( dcmp.diff_files) == 0: self.div_container.append(html.p('No Differences Found')) # handle left dir1 if len(dcmp.left_only) > 0: self.div_container.append(html.h3('Only in %s' % (dcmp.left, ))) ul_left = html.ul() for name in dcmp.left_only: ul_left.append(html.li(name)) self.div_container.append(ul_left) # handle right dir2 if len(dcmp.right_only) > 0: self.div_container.append(html.h3('Only in %s' % (dcmp.right, ))) ul_right = html.ul() for name in dcmp.right_only: ul_right.append(html.li(name)) self.div_container.append(ul_right) # handle diff between dir1 and dir2 for name in dcmp.diff_files: if self.is_binary_file(os.path.join(dcmp.left, name), 1024): self.div_container.append( html.table(html.thead( html.tr(html.th(os.path.join(dcmp.left, name)), html.th(os.path.join(dcmp.right, name)))), html.tbody( html.tr(html.td('Binary files differ'))), class_='table')) else: self.diff_file(os.path.join(dcmp.left, name), os.path.join(dcmp.right, name)) # handle sub-dirs for sub_dcmp in dcmp.subdirs.values(): self.handle_dcmp(sub_dcmp)
def pytest_html_results_table_header(cells): cells.insert(2, html.th('Description')) cells.insert(1, html.th('Time', class_='sortable time', col='time')) cells.pop()
def _generate_report(self, session): """ The method writes HTML report to a temporary logfile. """ suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() style_css = pkg_resources.resource_string( pytest_html_path, os.path.join('resources', 'style.css')) head = html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw(style_css))) summary = [html.h2('Summary'), html.p( '{0} tests ran in {1:.2f} seconds.'.format( numtests, suite_time_delta), html.br(), html.span('{0} passed'.format( self.passed), class_='passed'), ', ', html.span('{0} skipped'.format( self.skipped), class_='skipped'), ', ', html.span('{0} failed'.format( self.failed), class_='failed'), ', ', html.span('{0} errors'.format( self.errors), class_='error'), '.', html.br(), html.span('{0} expected failures'.format( self.xfailed), class_='skipped'), ', ', html.span('{0} unexpected passes'.format( self.xpassed), class_='failed'), '.')] results = [html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable initial-sort result', col='result'), html.th('Test', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(*self.test_logs, id='results-table-body')], id='results-table')] main_js = pkg_resources.resource_string( pytest_html_path, os.path.join('resources', 'main.js')) body = html.body( html.script(raw(main_js)), html.p('Report generated on {0} at {1}'.format( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')))) if session.config._environment: environment = set(session.config._environment) body.append(html.h2('Environment')) body.append(html.table( [html.tr(html.td(e[0]), html.td(e[1])) for e in sorted( environment, key=lambda e: e[0]) if e[1]], id='environment')) body.extend(summary) body.extend(results) doc = html.html(head, body) # A string which holds the complete report. report_content = ( "Test executed on commit " "[[https://www.github.com/tardis-sn/tardis/commit/{0}|{0}]]\n\n".format( tardis.__githash__ ) ) report_content += doc.unicode(indent=2) # Quick hack for preventing log to be placed in narrow left out space report_content = report_content.replace( u'class="log"', u'class="log" style="clear: both"' ) return report_content
def pytest_html_results_table_header(cells): cells.insert(1, html.th('Description')) cells.insert(2, html.th('Test_nodeid')) cells.pop(2)
def _generate_report(self, session): suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() class Outcome: def __init__( self, outcome, total=0, label=None, test_result=None, class_html=None ): self.outcome = outcome self.label = label or outcome self.class_html = class_html or outcome self.total = total self.test_result = test_result or outcome self.generate_checkbox() self.generate_summary_item() def generate_checkbox(self): checkbox_kwargs = {"data-test-result": self.test_result.lower()} if self.total == 0: checkbox_kwargs["disabled"] = "true" self.checkbox = html.input( type="checkbox", checked="true", onChange="filter_table(this)", name="filter_checkbox", class_="filter", hidden="true", **checkbox_kwargs, ) def generate_summary_item(self): self.summary_item = html.span( f"{self.total} {self.label}", class_=self.class_html ) outcomes = [ Outcome("passed", self.passed, label="通过"), Outcome("skipped", self.skipped, label="跳过"), Outcome("failed", self.failed, label="失败"), Outcome("error", self.errors, label="故障"), Outcome("xfailed", self.xfailed, label="预期的失败"), Outcome("xpassed", self.xpassed, label="未知的通过"), ] if self.rerun is not None: outcomes.append(Outcome("rerun", self.rerun, label="重跑")) summary = [ html.p( "(取消)勾选复选框, 以便筛选测试结果.", class_="filter", hidden="true", ), ] for i, outcome in enumerate(outcomes, start=1): summary.append(outcome.checkbox) summary.append(outcome.summary_item) if i < len(outcomes): summary.append(", ") cells = [ html.th("测试用例", class_="sortable initial-sort", col="name"), html.th("运行时间", class_="sortable numeric", col="duration"), html.th("运行结果", class_="sortable result", col="result"), html.th("日志资源"), ] session.config.hook.pytest_html_results_table_header(cells=cells) results = [ html.table( [ html.thead( html.tr(cells), html.tr( [ html.th( "无测试结果, 请考虑更换其他测试结果筛选条件.", colspan=len(cells), ) ], id="not-found-message", hidden="true", ), id="results-table-head", ), self.test_logs, ], id="results-table", ), ] session.config.hook.pytest_html_report_title(report=self) self.style_css = pkg_resources.resource_string( __name__, os.path.join("resources", "style.css") ).decode("utf-8") main_js = pkg_resources.resource_string( __name__, os.path.join("resources", "main.js") ).decode("utf-8") if ANSI: ansi_css = [ "\n/******************************", " * ANSI2HTML STYLES", " ******************************/\n", ] ansi_css.extend([str(r) for r in style.get_styles()]) self.style_css += "\n".join(ansi_css) # <DF> Add user-provided CSS for path in self.config.getoption("css"): self.style_css += "\n/******************************" self.style_css += "\n * CUSTOM CSS" self.style_css += f"\n * {path}" self.style_css += "\n ******************************/\n\n" with open(path, "r") as f: self.style_css += f.read() css_href = "assets/style.css" icon_href = "assets/favicon.png" html_icon = html.link(href=icon_href, rel="shortcut icon") html_css = html.link(href=css_href, rel="stylesheet", type="text/css") if self.self_contained: html_css = html.style(raw(self.style_css)) head = html.head( html.meta(charset="utf-8"), html.title(self.title), html_icon, html_css ) body = html.body( html.script(raw(main_js)), onLoad="init()", ) overview = [] if hasattr(self, "tester"): overview.append(html.p(html.strong("测试人员:"), self.tester)) if hasattr(self, "department"): overview.append(html.p(html.strong("测试中心:"), self.department)) overview.append( html.p( html.strong("用例统计:"), f"合计 {numtests} 条用例, ", f"运行时间为: {suite_time_delta:.2f} 秒, ", "生成时间为: {} {}".format( generated.strftime("%Y-%m-%d"), generated.strftime("%H:%M:%S") ), ) ) if hasattr(self, "description"): overview.append(html.p(html.strong("测试描述:"), self.description)) overview_html = html.div(overview, id="overview") body.extend( [ html.h1(self.title), html.h3(html.a(self.company["name"], href=self.company["url"])), overview_html, ] ) body.extend(self._generate_environment(session.config)) summary_prefix, summary_postfix = [], [] session.config.hook.pytest_html_results_summary( prefix=summary_prefix, summary=summary, postfix=summary_postfix ) body.extend([html.h2("测试结果详情")] + summary_prefix + summary + summary_postfix) body.extend(results) # Mail Template MailResult.title = self.title MailResult.count = numtests MailResult.passed = self.passed MailResult.failed = self.failed MailResult.xfailed = self.xfailed MailResult.xpassed = self.xpassed MailResult.errors = self.errors MailResult.skipped = self.skipped doc = html.html(head, body) unicode_doc = "<!DOCTYPE html>\n{}".format(doc.unicode(indent=2)) # Fix encoding issues, e.g. with surrogates unicode_doc = unicode_doc.encode("utf-8", errors="xmlcharrefreplace") return unicode_doc.decode("utf-8")
def html_stat(self, compare_to=None, hidenoise=0): if not compare_to: table = html.table( html.thead( html.tr( [ html.th(x, **{'mochi:format': y, 'align':'left'}) for (x,y) in [('Tests','str'), ('per run','float'), ('per oper.', 'float'), ('overhead', 'float')]]) ),id = "sortable_table") tests = self.tests.items() tests.sort() tbody = html.tbody() for name,t in tests: avg,op_avg,ov_avg = t.stat() tbody.append(html.tr( html.td(name), html.td(avg*1000.0), html.td(op_avg*1000000.0), html.td(ov_avg*1000.0) )) table.append(tbody) table.append(html.tr( 'Average round time %s' % (self.roundtime * 1000.0)) ) return table elif isinstance(compare_to, Benchmark): table = html.table(html.thead( html.tr([ html.th(x, **{'mochi:format': y, 'align':'left'}) for (x,y) in [('Tests','str'), ('per run','float'), ('per oper.', 'float'), ('diff', 'float')]])), id = "sortable_table", class_="datagrid") tests = self.tests.items() tests.sort() compatible = 1 tbody = html.tbody() for name,t in tests: avg,op_avg,ov_avg = t.stat() try: other = compare_to.tests[name] except KeyError: other = None if other and other.version == t.version and \ other.operations == t.operations: avg1,op_avg1,ov_avg1 = other.stat() qop_avg = (op_avg/op_avg1-1.0)*100.0 if hidenoise and abs(qop_avg) < 10: qop_avg = '' else: qop_avg = '%+7.2f%%' % qop_avg else: qavg,qop_avg = 'n/a', 'n/a' compatible = 0 tbody.append(html.tr( html.td(name), html.td(avg*1000.0), html.td(op_avg*1000000.0), html.td(qop_avg) )) if compatible and compare_to.roundtime > 0 and \ compare_to.version == self.version: tbody.append(html.tr( html.td('Average round time'), html.td(self.roundtime * 1000.0), html.td(''), html.td('%+7.2f%%'% (((self.roundtime*self.warp)/ (compare_to.roundtime*compare_to.warp)-1.0)*100.0) ))) else: tbody.append(html.tr( html.td('Average round time'), html.td(self.roundtime * 1000.0))) table.append(tbody) return table else: table = html.table(html.thead( html.tr([ html.th(x, **{'mochi:format': y, 'align':'left'}) for (x,y) in [('Tests','str')]+[('pypy ver','float') for z in compare_to] ])), id = "sortable_table") tests = self.tests.items() tests.sort() compatible = 1 for name,t in tests: avg,op_avg,ov_avg = t.stat() percent = [] for comp_to in compare_to: try: other = comp_to.tests[name] except KeyError: other = None if other and other.version == t.version and \ other.operations == t.operations: avg1,op_avg1,ov_avg1 = other.stat() qop_avg = (op_avg/op_avg1-1.0)*100.0 if hidenoise and abs(qop_avg) < 10: qop_avg = '' else: qop_avg = '%+7.2f%%' % qop_avg else: qavg,qop_avg = 'n/a', 'n/a' compatible = 0 percent.append(qop_avg) table.append(html.tr( html.td(name), [html.td(qop_avg) for qop_avg in percent] )) if compatible and compare_to.roundtime > 0 and \ compare_to.version == self.version: table.append(html.tr( html.td('Average round time'), html.td(self.roundtime * 1000.0), html.td(''), html.td('%+7.2f%%'% (((self.roundtime*self.warp)/ (compare_to.roundtime*compare_to.warp)-1.0)*100.0) ))) else: table.append(html.tr( html.td('Average round time'), html.td(self.roundtime * 1000.0))) return table
def pytest_html_results_table_header(cells): cells.insert(1, html.th("Specification", class_="spec", col="spec")) cells.insert(2, html.th("Description")) cells.insert(3, html.th("Procedure", class_="proc", col="proc")) cells.insert(4, html.th("Expected", class_="expec", col="expec")) cells.pop() # remove links column
def pytest_sessionfinish(self): if not os.path.exists(os.path.dirname(self.logfile)): os.makedirs(os.path.dirname(self.logfile)) logfile = open(self.logfile, "w", encoding="utf-8") suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() style_css = pkg_resources.resource_string(__name__, os.path.join("resources", "style.css")) if PY3: style_css = style_css.decode("utf-8") head = html.head(html.meta(charset="utf-8"), html.title("Test Report"), html.style(raw(style_css))) summary = [ html.h2("Summary"), html.p( "%i tests ran in %.2f seconds." % (numtests, suite_time_delta), html.br(), html.span("%i passed" % self.passed, class_="passed"), ", ", html.span("%i skipped" % self.skipped, class_="skipped"), ", ", html.span("%i failed" % self.failed, class_="failed"), ", ", html.span("%i errors" % self.errors, class_="error"), ".", html.br(), html.span("%i expected failures" % self.xfailed, class_="skipped"), ", ", html.span("%i unexpected passes" % self.xpassed, class_="failed"), ".", ), ] self._create_overview() overview = [ html.h2("Overview"), html.table( [ html.thead( html.tr( [ html.th("Test", col="name"), html.th("Setup", col="setup"), html.th("Call", col="call"), html.th("Teardown", col="teardown"), ] ), id="overview-table-head", ), html.tbody(*self.test_overview, id="overview-table-body"), ], id="overview-table", ), ] results = [ html.h2("Results"), html.table( [ html.thead( html.tr( [ html.th("Result", class_="sortable initial-sort result", col="result"), html.th("Test", class_="sortable", col="name"), html.th("Duration", class_="sortable numeric", col="duration"), # html.th('Links') ] ), id="results-table-head", ), html.tbody(*self.test_logs, id="results-table-body"), ], id="results-table", ), ] main_js = pkg_resources.resource_string(__name__, os.path.join("resources", "main.js")) if PY3: main_js = main_js.decode("utf-8") body = html.body( html.script(raw(main_js)), html.p("Report generated on %s at %s" % (generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S"))), ) environment = {} for e in self.environment: for k, v in e.items(): environment[k] = v if environment: body.append(html.h2("Environment")) body.append( html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(environment.items()) if v], id="environment" ) ) body.extend(summary) body.extend(overview) body.extend(results) doc = html.html(head, body) logfile.write("<!DOCTYPE html>") logfile.write(doc.unicode(indent=2)) logfile.close()
def _generate_report(self, session): suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() style_css = pkg_resources.resource_string( __name__, os.path.join('resources', 'style.css')) if PY3: style_css = style_css.decode('utf-8') head = html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw(style_css))) class Outcome: def __init__(self, outcome, total=0, label=None, test_result=None, class_html=None): self.outcome = outcome self.label = label or outcome self.class_html = class_html or outcome self.total = total self.test_result = test_result or outcome self.generate_checkbox() self.generate_summary_item() def generate_checkbox(self): checkbox_kwargs = {'data-test-result': self.test_result.lower()} if self.total == 0: checkbox_kwargs['disabled'] = 'true' self.checkbox = html.input(type='checkbox', checked='true', onChange='filter_table(this)', name='filter_checkbox', **checkbox_kwargs) def generate_summary_item(self): self.summary_item = html.span('{0} {1}'. format(self.total, self.label), class_=self.class_html) outcomes = [Outcome('passed', self.passed), Outcome('skipped', self.skipped), Outcome('failed', self.failed), Outcome('error', self.errors, label='errors'), Outcome('xfailed', self.xfailed, label='expected failures'), Outcome('xpassed', self.xpassed, label='unexpected passes'), Outcome('rerun', self.rerun)] summary = [html.h2('Summary'), html.p( '{0} tests ran in {1:.2f} seconds. '.format( numtests, suite_time_delta)), html.p('(Un)check the boxes to filter the results.')] for outcome in outcomes: summary.append(outcome.checkbox) summary.append(outcome.summary_item) summary.append(' ') results = [html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable initial-sort result', col='result'), html.th('Test', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), html.tr([ html.th('No results found. Try to check the filters', colspan='5')], id='not-found-message', hidden='true'), id='results-table-head'), self.test_logs], id='results-table')] main_js = pkg_resources.resource_string( __name__, os.path.join('resources', 'main.js')) if PY3: main_js = main_js.decode('utf-8') body = html.body( html.script(raw(main_js)), html.p('Report generated on {0} at {1}'.format( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')))) if session.config._environment: environment = set(session.config._environment) body.append(html.h2('Environment')) body.append(html.table( [html.tr(html.td(e[0]), html.td(e[1])) for e in sorted( environment, key=lambda e: e[0]) if e[1]], id='environment')) body.extend(summary) body.extend(results) doc = html.html(head, body) unicode_doc = u'<!DOCTYPE html>\n{0}'.format(doc.unicode(indent=2)) if PY3: # Fix encoding issues, e.g. with surrogates unicode_doc = unicode_doc.encode('utf-8', errors='xmlcharrefreplace') unicode_doc = unicode_doc.decode('utf-8') return unicode_doc
generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), 'v0.0.1'), html.h2('Configuration'), html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(configuration.items()) if v], id='configuration'), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (numtests , suite_time_delta ), #'''numtests''' '''suite_time_delta''' html.br(), html.span('%i passed' % passed, class_='passed'), ', ', #'self.passed' html.span('%i skipped' % skipped, class_='skipped'), ', ', #'self.skipped' html.span('%i failed' % failed, class_='failed'), ', ', #'self.failed' html.span('%i errors' % errors, class_='error'), '.', #'self.errors' html.br()), #html.span('%i expected failures' % 0, class_='skipped'), ', ', #'self.xfailed' #html.span('%i unexpected passes' % 0, class_='failed'), '.'), #'self.xpassed' html.h2('Results'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(*test_logs, id='results-table-body')], id='results-table')))) # Write HTML report file logfile.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' + doc.unicode(indent=2)) logfile.close()
def pytest_sessionfinish(self): if not os.path.exists(os.path.dirname(self.logfile)): os.makedirs(os.path.dirname(self.logfile)) logfile = open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() style_css = pkg_resources.resource_string( __name__, os.path.join('resources', 'style.css')) if PY3: style_css = style_css.decode('utf-8') head = html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw(style_css))) summary = [html.h2('Summary'), html.p( '%i tests ran in %.2f seconds.' % (numtests, suite_time_delta), html.br(), html.span('%i passed' % self.passed, class_='passed'), ', ', html.span('%i skipped' % self.skipped, class_='skipped'), ', ', html.span('%i failed' % self.failed, class_='failed'), ', ', html.span('%i errors' % self.errors, class_='error'), '.', html.br(), html.span('%i expected failures' % self.xfailed, class_='skipped'), ', ', html.span('%i unexpected passes' % self.xpassed, class_='failed'), '.')] results = [html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable initial-sort result', col='result'), html.th('Test', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(*self.test_logs, id='results-table-body')], id='results-table')] main_js = pkg_resources.resource_string( __name__, os.path.join('resources', 'main.js')) if PY3: main_js = main_js.decode('utf-8') body = html.body( html.script(raw(main_js)), html.p('Report generated on %s at %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')))) environment = {} for e in self.environment: for k, v in e.items(): environment[k] = v if environment: body.append(html.h2('Environment')) body.append(html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted( environment.items()) if v], id='environment')) body.extend(summary) body.extend(results) doc = html.html(head, body) logfile.write('<!DOCTYPE html>') logfile.write(doc.unicode(indent=2)) logfile.close()
def pytest_sessionfinish(self, session, exitstatus, __multicall__): self._make_report_dir() logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed server = self.config.option.sauce_labs_credentials_file and \ 'Sauce Labs' or 'http://%s:%s' % (self.config.option.host, self.config.option.port) browser = self.config.option.browser_name and \ self.config.option.browser_version and \ self.config.option.platform and \ '%s %s on %s' % (str(self.config.option.browser_name).title(), self.config.option.browser_version, str(self.config.option.platform).title()) or \ self.config.option.environment or \ self.config.option.browser generated = datetime.datetime.now() configuration = { 'Base URL': self.config.option.base_url, 'Build': self.config.option.build, 'Selenium API': self.config.option.api, 'Driver': self.config.option.driver, 'Firefox Path': self.config.option.firefox_path, 'Google Chrome Path': self.config.option.chrome_path, 'Selenium Server': server, 'Browser': browser, 'Timeout': self.config.option.webqatimeout, 'Capture Network Traffic': self.config.option.capture_network, 'Credentials': self.config.option.credentials_file, 'Sauce Labs Credentials': self.config.option.sauce_labs_credentials_file} import pytest_mozwebqa doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.link(rel='stylesheet', href='style.css'), html.script(src='jquery.js'), html.script(src='main.js')), html.body( html.p('Report generated on %s at %s by pytest-mozwebqa %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), pytest_mozwebqa.__version__)), html.h2('Configuration'), html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(configuration.items()) if v], id='configuration'), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (numtests, suite_time_delta), html.br(), html.span('%i passed' % self.passed, class_='passed'), ', ', html.span('%i skipped' % self.skipped, class_='skipped'), ', ', html.span('%i failed' % self.failed, class_='failed'), ', ', html.span('%i errors' % self.errors, class_='error'), '.', html.br(), html.span('%i expected failures' % self.xfailed, class_='skipped'), ', ', html.span('%i unexpected passes' % self.xpassed, class_='failed'), '.'), html.h2('Results'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(*self.test_logs, id='results-table-body')], id='results-table'))) logfile.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' + doc.unicode(indent=2)) logfile.close()
def generate_html(self, results_list): def failed_count(results): count = len(results.failures) if hasattr(results, 'unexpectedSuccesses'): count += len(results.unexpectedSuccesses) return count tests = sum([results.testsRun for results in results_list]) failures = sum([failed_count(results) for results in results_list]) skips = sum([len(results.skipped) + len(results.expectedFailures) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = 0 test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, text='', result='passed', debug=None): cls_name = test.__class__.__name__ tc_name = unicode(test).split()[0] tc_time = str(test.duration) additional_html = [] links_html = [] if result in ['failure', 'error', 'skipped']: if debug and debug.get('screenshot'): screenshot = 'data:image/png;base64,%s' % debug['screenshot'] additional_html.append( html.div( html.a(html.img(src=screenshot), href=screenshot), class_='screenshot')) for name, content in debug.items(): try: if 'screenshot' in name: links_html.append(html.a(name, href='data:image/png;base64,%s' % content.encode('us-ascii'))) else: # use base64 to avoid that some browser(such as Firefox, Opera) treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. links_html.append(html.a(name, href='data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content))) links_html.append(' ') except: pass log = html.div(class_=result) for line in text.splitlines(): separator = line.startswith(' ' * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower().find("exception") != -1: log.append(html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append(html.tr([ html.td(result, class_='col-result'), html.td(cls_name, class_='col-class'), html.td(tc_name, class_='col-name'), html.td(tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug')], class_=result.lower() + ' results-table-row')) for results in results_list: for test in results.tests_passed: _extract_html(test) passes = passes + 1 for result in results.failures: _extract_html(result[0], text=result[1], result='failure', debug=result[2]) for result in results.errors: _extract_html(result[0], text=result[1], result='error', debug=result[2]) jquery_src = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'jquery.js')) main_src = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'main.js')) style_src = os.path.abspath(os.path.join(os.path.dirname(__file__), 'resources', 'style.css')) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.link(rel='stylesheet', href=style_src), html.script(src=jquery_src), html.script(src=main_src)), html.body( html.p('Report generated on %s at %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'))), html.table( html.h2('Summary'), html.p('%i tests ran. in %i seconds' % (tests, test_time), html.br(), html.span('%i passed' % passes, class_='passed'), ', ', html.span('%i failed' % failures, class_='failed'), ', ', html.span('%i skipped' % skips, class_='skipped'), ', ', html.span('%i error' % errors, class_='error'), html.br()), html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(test_logs, id='results-table-body')], id='results-table') ) ) ) return doc.unicode(indent=2)
html.br(), html.span('%i passed' % passed, class_='passed'), ', ', #'self.passed' html.span('%i skipped' % skipped, class_='skipped'), ', ', #'self.skipped' html.span('%i failed' % failed, class_='failed'), ', ', #'self.failed' html.span('%i errors' % errors, class_='error'), '.', #'self.errors' html.br()), #html.span('%i expected failures' % 0, class_='skipped'), ', ', #'self.xfailed' #html.span('%i unexpected passes' % 0, class_='failed'), '.'), #'self.xpassed' html.h2('Results'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Name', class_='sortable', col='name'), html.th( 'Duration', class_='sortable numeric', col='duration'), html.th('Links') ]), id='results-table-head'), html.tbody(*test_logs, id='results-table-body') ], id='results-table')))) # Write HTML report file logfile.write( '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' + doc.unicode(indent=2))
def generate_html(self, results_list): tests = sum([results.testsRun for results in results_list]) failures = sum([len(results.failures) for results in results_list]) expected_failures = sum( [len(results.expectedFailures) for results in results_list]) skips = sum([len(results.skipped) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = sum([results.passed for results in results_list]) unexpected_passes = sum( [len(results.unexpectedSuccesses) for results in results_list]) test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, class_name, duration=0, text='', result='passed', debug=None): cls_name = class_name tc_name = unicode(test) tc_time = duration additional_html = [] debug = debug or {} links_html = [] if result in ['skipped', 'failure', 'expected failure', 'error']: if debug.get('screenshot'): screenshot = 'data:image/png;base64,%s' % debug[ 'screenshot'] additional_html.append( html.div(html.a(html.img(src=screenshot), href="#"), class_='screenshot')) for name, content in debug.items(): try: if 'screenshot' in name: href = '#' else: # use base64 to avoid that some browser (such as Firefox, Opera) # treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode( content) links_html.append( html.a(name.title(), class_=name, href=href, target='_blank')) links_html.append(' ') except: pass log = html.div(class_='log') for line in text.splitlines(): separator = line.startswith(' ' * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower( ).find("exception") != -1: log.append( html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append( html.tr([ html.td(result.title(), class_='col-result'), html.td(cls_name, class_='col-class'), html.td(tc_name, class_='col-name'), html.td(tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug') ], class_=result.lower() + ' results-table-row')) for results in results_list: for test in results.tests_passed: _extract_html(test.name, test.test_class) for result in results.skipped: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='skipped') for result in results.failures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='failure', debug=result.debug) for result in results.expectedFailures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='expected failure', debug=result.debug) for test in results.unexpectedSuccesses: _extract_html(test.name, test.test_class, result='unexpected pass') for result in results.errors: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='error', debug=result.debug) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw( pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'style.css']))), type='text/css')), html.body( html.script(raw( pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'jquery.js']))), type='text/javascript'), html.script(raw( pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'main.js']))), type='text/javascript'), html.p( 'Report generated on %s at %s by %s %s' % (generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), __name__, __version__)), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (tests, test_time), html.br(), html.span('%i passed' % passes, class_='passed'), ', ', html.span('%i skipped' % skips, class_='skipped'), ', ', html.span('%i failed' % failures, class_='failed'), ', ', html.span('%i errors' % errors, class_='error'), '.', html.br(), html.span('%i expected failures' % expected_failures, class_='expected failure'), ', ', html.span('%i unexpected passes' % unexpected_passes, class_='unexpected pass'), '.'), html.h2('Results'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links') ]), id='results-table-head'), html.tbody(test_logs, id='results-table-body') ], id='results-table'))) return doc.unicode(indent=2)
def pytest_html_results_table_header(cells): '''Adding columns to HTML Report, Description and Time Executed ''' cells.insert(2, html.th('Description')) cells.insert( 1, html.th('Local Time Executed', class_='sortable time', col='time'))
def _generate_report(self, session): suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() self.style_css = pkg_resources.resource_string( __name__, os.path.join("resources", "style.css")).decode("utf-8") if ANSI: ansi_css = [ "\n/******************************", " * ANSI2HTML STYLES", " ******************************/\n", ] ansi_css.extend([str(r) for r in style.get_styles()]) self.style_css += "\n".join(ansi_css) # <DF> Add user-provided CSS for path in self.config.getoption("css"): self.style_css += "\n/******************************" self.style_css += "\n * CUSTOM CSS" self.style_css += f"\n * {path}" self.style_css += "\n ******************************/\n\n" with open(path, "r") as f: self.style_css += f.read() css_href = "assets/style.css" html_css = html.link(href=css_href, rel="stylesheet", type="text/css") if self.self_contained: html_css = html.style(raw(self.style_css)) head = html.head( html.meta(charset="utf-8"), html.meta( content= "width=device-width, initial-scale=1.0, minimum-scale=0.5, maximum-scale=2.0, user-scalable=no" ), html.title("Test Report"), html_css) class Outcome: def __init__(self, outcome, total=0, label=None, test_result=None, class_html=None): self.outcome = outcome self.label = label or outcome self.class_html = class_html or outcome self.total = total self.test_result = test_result or outcome self.generate_checkbox() self.generate_summary_item() def generate_checkbox(self): checkbox_kwargs = { "data-test-result": self.test_result.lower() } if self.total == 0: checkbox_kwargs["disabled"] = "true" self.checkbox = html.input( type="checkbox", checked="true", onChange="filter_table(this)", name="filter_checkbox", class_="filter", hidden="true", **checkbox_kwargs, ) def generate_summary_item(self): self.summary_item = html.span(f"{self.total} {self.label}", class_=self.class_html) outcomes = [ Outcome("passed", self.passed), Outcome("skipped", self.skipped), Outcome("failed", self.failed), Outcome("error", self.errors, label="errors"), Outcome("xfailed", self.xfailed, label="expected failures"), Outcome("xpassed", self.xpassed, label="unexpected passes"), ] if self.rerun is not None: outcomes.append(Outcome("rerun", self.rerun)) summary = [ html.p( f"{numtests} tests ran in {suite_time_delta:.2f} seconds. "), html.p( "(Un)check the boxes to filter the results.", class_="filter", hidden="true", ), ] for i, outcome in enumerate(outcomes, start=1): summary.append(outcome.checkbox) summary.append(outcome.summary_item) if i < len(outcomes): summary.append(", ") cells = [ html.th("Result", class_="sortable result initial-sort", col="result"), html.th("Test", class_="sortable", col="name"), html.th("Duration", class_="sortable numeric", col="duration"), html.th("Links"), ] session.config.hook.pytest_html_results_table_header(cells=cells) results = [ html.h2("Results"), html.table( [ html.thead( html.tr(cells), html.tr( [ html.th( "No results found. Try to check the filters", colspan=len(cells), ) ], id="not-found-message", hidden="true", ), id="results-table-head", ), self.test_logs, ], id="results-table", ), ] main_js = pkg_resources.resource_string( __name__, os.path.join("resources", "main.js")).decode("utf-8") report_title = self.logfile.split('/')[-4] + '测试报告如下:' body = html.body( html.script(raw(main_js)), # html.h1(os.path.basename(self.logfile)), html.h1(report_title), # 测试报告title html. p("Report generated on {} at {}.".format( generated.strftime("%Y-%m-%d"), generated.strftime("%H:%M:%S")), # html.a("pytest-html", href=__pypi_url__), # f" v{__version__}", ), onLoad="init()", ) body.extend(self._generate_environment(session.config)) summary_prefix, summary_postfix = [], [] session.config.hook.pytest_html_results_summary( prefix=summary_prefix, summary=summary, postfix=summary_postfix) body.extend([html.h2("Summary")] + summary_prefix + summary + summary_postfix) body.extend(results) doc = html.html(head, body) unicode_doc = "<!DOCTYPE html>\n{}".format(doc.unicode(indent=2)) # Fix encoding issues, e.g. with surrogates unicode_doc = unicode_doc.encode("utf-8", errors="xmlcharrefreplace") return unicode_doc.decode("utf-8")
def _generate_report(self, session): suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() self.style_css = pkg_resources.resource_string( __name__, os.path.join('resources', 'style.css')) if PY3: self.style_css = self.style_css.decode('utf-8') if ANSI: ansi_css = [ '\n/******************************', ' * ANSI2HTML STYLES', ' ******************************/\n'] ansi_css.extend([str(r) for r in style.get_styles()]) self.style_css += '\n'.join(ansi_css) # <DF> Add user-provided CSS for path in self.config.getoption('css') or []: self.style_css += '\n/******************************' self.style_css += '\n * CUSTOM CSS' self.style_css += '\n * {}'.format(path) self.style_css += '\n ******************************/\n\n' with open(path, 'r') as f: self.style_css += f.read() css_href = '{0}/{1}'.format('assets', 'style.css') html_css = html.link(href=css_href, rel='stylesheet', type='text/css') if self.self_contained: html_css = html.style(raw(self.style_css)) head = html.head( html.meta(charset='utf-8'), html.title('Test Report'), html_css) class Outcome: def __init__(self, outcome, total=0, label=None, test_result=None, class_html=None): self.outcome = outcome self.label = label or outcome self.class_html = class_html or outcome self.total = total self.test_result = test_result or outcome self.generate_checkbox() self.generate_summary_item() def generate_checkbox(self): checkbox_kwargs = {'data-test-result': self.test_result.lower()} if self.total == 0: checkbox_kwargs['disabled'] = 'true' self.checkbox = html.input(type='checkbox', checked='true', onChange='filter_table(this)', name='filter_checkbox', class_='filter', hidden='true', **checkbox_kwargs) def generate_summary_item(self): self.summary_item = html.span('{0} {1}'. format(self.total, self.label), class_=self.class_html) outcomes = [Outcome('passed', self.passed), Outcome('skipped', self.skipped), Outcome('failed', self.failed), Outcome('error', self.errors, label='errors'), Outcome('xfailed', self.xfailed, label='expected failures'), Outcome('xpassed', self.xpassed, label='unexpected passes')] if self.rerun is not None: outcomes.append(Outcome('rerun', self.rerun)) summary = [html.p( '{0} tests ran in {1:.2f} seconds. '.format( numtests, suite_time_delta)), html.p('(Un)check the boxes to filter the results.', class_='filter', hidden='true')] for i, outcome in enumerate(outcomes, start=1): summary.append(outcome.checkbox) summary.append(outcome.summary_item) if i < len(outcomes): summary.append(', ') cells = [ html.th('Result', class_='sortable result initial-sort', col='result'), html.th('Test', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')] session.config.hook.pytest_html_results_table_header(cells=cells) results = [html.h2('Results'), html.table([html.thead( html.tr(cells), html.tr([ html.th('No results found. Try to check the filters', colspan=len(cells))], id='not-found-message', hidden='true'), id='results-table-head'), self.test_logs], id='results-table')] main_js = pkg_resources.resource_string( __name__, os.path.join('resources', 'main.js')) if PY3: main_js = main_js.decode('utf-8') body = html.body( html.script(raw(main_js)), html.h1(os.path.basename(session.config.option.htmlpath)), html.p('Report generated on {0} at {1} by'.format( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')), html.a(' pytest-html', href=__pypi_url__), ' v{0}'.format(__version__)), onLoad='init()') body.extend(self._generate_environment(session.config)) summary_prefix, summary_postfix = [], [] session.config.hook.pytest_html_results_summary( prefix=summary_prefix, summary=summary, postfix=summary_postfix) body.extend([html.h2('Summary')] + summary_prefix + summary + summary_postfix) body.extend(results) doc = html.html(head, body) unicode_doc = u'<!DOCTYPE html>\n{0}'.format(doc.unicode(indent=2)) if PY3: # Fix encoding issues, e.g. with surrogates unicode_doc = unicode_doc.encode('utf-8', errors='xmlcharrefreplace') unicode_doc = unicode_doc.decode('utf-8') return unicode_doc
def generate_html(self, results_list): tests = sum([results.testsRun for results in results_list]) failures = sum([len(results.failures) for results in results_list]) expected_failures = sum([len(results.expectedFailures) for results in results_list]) skips = sum([len(results.skipped) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = sum([results.passed for results in results_list]) unexpected_passes = sum([len(results.unexpectedSuccesses) for results in results_list]) test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, class_name, duration=0, text="", result="passed", debug=None): cls_name = class_name tc_name = unicode(test) tc_time = duration additional_html = [] debug = debug or {} links_html = [] if result in ["skipped", "failure", "expected failure", "error"]: if debug.get("screenshot"): screenshot = "data:image/png;base64,%s" % debug["screenshot"] additional_html.append(html.div(html.a(html.img(src=screenshot), href="#"), class_="screenshot")) for name, content in debug.items(): try: if "screenshot" in name: href = "#" else: # use base64 to avoid that some browser (such as Firefox, Opera) # treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. href = "data:text/plain;charset=utf-8;base64,%s" % base64.b64encode(content) links_html.append(html.a(name.title(), class_=name, href=href, target="_blank")) links_html.append(" ") except: pass log = html.div(class_="log") for line in text.splitlines(): separator = line.startswith(" " * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower().find("exception") != -1: log.append(html.span(raw(cgi.escape(line)), class_="error")) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append( html.tr( [ html.td(result.title(), class_="col-result"), html.td(cls_name, class_="col-class"), html.td(tc_name, class_="col-name"), html.td(tc_time, class_="col-duration"), html.td(links_html, class_="col-links"), html.td(additional_html, class_="debug"), ], class_=result.lower() + " results-table-row", ) ) for results in results_list: for test in results.tests_passed: _extract_html(test.name, test.test_class) for result in results.skipped: _extract_html(result.name, result.test_class, text="\n".join(result.output), result="skipped") for result in results.failures: _extract_html( result.name, result.test_class, text="\n".join(result.output), result="failure", debug=result.debug ) for result in results.expectedFailures: _extract_html( result.name, result.test_class, text="\n".join(result.output), result="expected failure", debug=result.debug, ) for test in results.unexpectedSuccesses: _extract_html(test.name, test.test_class, result="unexpected pass") for result in results.errors: _extract_html( result.name, result.test_class, text="\n".join(result.output), result="error", debug=result.debug ) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset="utf-8"), html.title("Test Report"), html.style( raw( pkg_resources.resource_string(__name__, os.path.sep.join(["resources", "report", "style.css"])) ), type="text/css", ), ), html.body( html.script( raw( pkg_resources.resource_string(__name__, os.path.sep.join(["resources", "report", "jquery.js"])) ), type="text/javascript", ), html.script( raw(pkg_resources.resource_string(__name__, os.path.sep.join(["resources", "report", "main.js"]))), type="text/javascript", ), html.p( "Report generated on %s at %s by %s %s" % (generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S"), __name__, __version__) ), html.h2("Summary"), html.p( "%i tests ran in %i seconds." % (tests, test_time), html.br(), html.span("%i passed" % passes, class_="passed"), ", ", html.span("%i skipped" % skips, class_="skipped"), ", ", html.span("%i failed" % failures, class_="failed"), ", ", html.span("%i errors" % errors, class_="error"), ".", html.br(), html.span("%i expected failures" % expected_failures, class_="expected failure"), ", ", html.span("%i unexpected passes" % unexpected_passes, class_="unexpected pass"), ".", ), html.h2("Results"), html.table( [ html.thead( html.tr( [ html.th("Result", class_="sortable", col="result"), html.th("Class", class_="sortable", col="class"), html.th("Test Name", class_="sortable", col="name"), html.th("Duration", class_="sortable numeric", col="duration"), html.th("Links"), ] ), id="results-table-head", ), html.tbody(test_logs, id="results-table-body"), ], id="results-table", ), ), ) return doc.unicode(indent=2)
def generate_html(self, results_list): tests = sum([results.testsRun for results in results_list]) failures = sum([len(results.failures) for results in results_list]) expected_failures = sum([len(results.expectedFailures) for results in results_list]) skips = sum([len(results.skipped) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = sum([results.passed for results in results_list]) unexpected_passes = sum([len(results.unexpectedSuccesses) for results in results_list]) test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, class_name, duration=0, text='', result='passed', debug=None): cls_name = class_name tc_name = unicode(test) tc_time = duration additional_html = [] debug = debug or {} links_html = [] if result in ['skipped', 'failure', 'expected failure', 'error']: if debug.get('screenshot'): screenshot = 'data:image/png;base64,%s' % debug['screenshot'] additional_html.append(html.div( html.a(html.img(src=screenshot), href="#"), class_='screenshot')) for name, content in debug.items(): try: if 'screenshot' in name: href = '#' else: # use base64 to avoid that some browser (such as Firefox, Opera) # treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content) links_html.append(html.a( name.title(), class_=name, href=href, target='_blank')) links_html.append(' ') except: pass log = html.div(class_='log') for line in text.splitlines(): separator = line.startswith(' ' * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower().find("exception") != -1: log.append(html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append(html.tr([ html.td(result.title(), class_='col-result'), html.td(cls_name, class_='col-class'), html.td(tc_name, class_='col-name'), html.td(tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug')], class_=result.lower() + ' results-table-row')) for results in results_list: for test in results.tests_passed: _extract_html(test.name, test.test_class) for result in results.skipped: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='skipped') for result in results.failures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='failure', debug=result.debug) for result in results.expectedFailures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='expected failure', debug=result.debug) for test in results.unexpectedSuccesses: _extract_html(test.name, test.test_class, result='unexpected pass') for result in results.errors: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='error', debug=result.debug) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw(pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'style.css']))), type='text/css')), html.body( html.script(raw(pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'jquery.js']))), type='text/javascript'), html.script(raw(pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'main.js']))), type='text/javascript'), html.p('Report generated on %s at %s by %s %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), __name__, __version__)), html.h2('Summary'), html.p('%i tests ran in %i seconds.' % (tests, test_time), html.br(), html.span('%i passed' % passes, class_='passed'), ', ', html.span('%i skipped' % skips, class_='skipped'), ', ', html.span('%i failed' % failures, class_='failed'), ', ', html.span('%i errors' % errors, class_='error'), '.', html.br(), html.span('%i expected failures' % expected_failures, class_='expected failure'), ', ', html.span('%i unexpected passes' % unexpected_passes, class_='unexpected pass'), '.'), html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(test_logs, id='results-table-body')], id='results-table'))) return doc.unicode(indent=2)
def pytest_sessionfinish(self, session, exitstatus, __multicall__): self._make_report_dir() logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed server = self.config.option.sauce_labs_credentials_file and \ 'Sauce Labs' or 'http://%s:%s' % (self.config.option.host, self.config.option.port) browser = self.config.option.browser_name and \ self.config.option.browser_version and \ self.config.option.platform and \ '%s %s on %s' % (str(self.config.option.browser_name).title(), self.config.option.browser_version, str(self.config.option.platform).title()) or \ self.config.option.environment or \ self.config.option.browser generated = datetime.datetime.now() configuration = { 'Base URL': self.config.option.base_url, 'Build': self.config.option.build, 'Selenium API': self.config.option.api, 'Driver': self.config.option.driver, 'Firefox Path': self.config.option.firefox_path, 'Google Chrome Path': self.config.option.chrome_path, 'Selenium Server': server, 'Browser': browser, 'Timeout': self.config.option.webqatimeout, 'Capture Network Traffic': self.config.option.capture_network, 'Credentials': self.config.option.credentials_file, 'Sauce Labs Credentials': self.config.option.sauce_labs_credentials_file} import pytest_mozwebqa doc = html.html( html.head( html.title('Test Report'), html.style( 'body {font-family: Helvetica, Arial, sans-serif; font-size: 12px}\n', 'body * {box-sizing: -moz-border-box; box-sizing: -webkit-border-box; box-sizing: border-box}\n', 'a {color: #999}\n', 'h2 {font-size: 16px}\n', 'table {border: 1px solid #e6e6e6; color: #999; font-size: 12px; border-collapse: collapse}\n', '#configuration tr:nth-child(odd) {background-color: #f6f6f6}\n', '#results {width:100%}\n', 'th, td {padding: 5px; border: 1px solid #E6E6E6; text-align: left}\n', 'th {font-weight: bold}\n', 'tr.passed, tr.skipped, tr.xfailed, tr.error, tr.failed, tr.xpassed {color: inherit}\n' 'tr.passed + tr.additional {display: none}\n', '.passed {color: green}\n', '.skipped, .xfailed {color: orange}\n', '.error, .failed, .xpassed {color: red}\n', '.log:only-child {height: inherit}\n', raw('.log {background-color: #e6e6e6; border: 1px solid #e6e6e6; color: black; display: block; font-family: "Courier New", Courier, monospace; height: 230px; overflow-y: scroll; padding: 5px; white-space:pre-wrap}\n'), '.screenshot, .video {border: 1px solid #e6e6e6; float:right; height:240px; margin-left:5px; overflow:hidden; width:320px}\n', '.screenshot img {width: 320px}')), html.body( html.p('Report generated on %s at %s by pytest-mozwebqa %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), pytest_mozwebqa.__version__)), html.h2('Configuration'), html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(configuration.items()) if v], id='configuration'), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (numtests, suite_time_delta), html.br(), html.span('%i passed' % self.passed, class_='passed'), ', ', html.span('%i skipped' % self.skipped, class_='skipped'), ', ', html.span('%i failed' % self.failed, class_='failed'), ', ', html.span('%i errors' % self.errors, class_='error'), '.', html.br(), html.span('%i expected failures' % self.xfailed, class_='skipped'), ', ', html.span('%i unexpected passes' % self.xpassed, class_='failed'), '.'), html.h2('Results'), html.table( html.tr(html.th('Result'), html.th('Class'), html.th('Name'), html.th('Duration'), html.th('Links')), *self.test_logs, id='results'))) logfile.write(doc.unicode(indent=2)) logfile.close()
def pytest_sessionfinish(self, session): if not os.path.exists(os.path.dirname(self.logfile)): os.makedirs(os.path.dirname(self.logfile)) logfile = open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() style_css = pkg_resources.resource_string( __name__, os.path.join('resources', 'style.css')) if PY3: style_css = style_css.decode('utf-8') head = html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw(style_css))) summary = [html.h2('Summary'), html.p( '{0} tests ran in {1:.2f} seconds.'.format( numtests, suite_time_delta), html.br(), html.span('{0} passed'.format( self.passed), class_='passed'), ', ', html.span('{0} skipped'.format( self.skipped), class_='skipped'), ', ', html.span('{0} failed'.format( self.failed), class_='failed'), ', ', html.span('{0} errors'.format( self.errors), class_='error'), '.', html.br(), html.span('{0} expected failures'.format( self.xfailed), class_='skipped'), ', ', html.span('{0} unexpected passes'.format( self.xpassed), class_='failed'), '.')] results = [html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable initial-sort result', col='result'), html.th('Test', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(*self.test_logs, id='results-table-body')], id='results-table')] main_js = pkg_resources.resource_string( __name__, os.path.join('resources', 'main.js')) if PY3: main_js = main_js.decode('utf-8') body = html.body( html.script(raw(main_js)), html.p('Report generated on {0} at {1}'.format( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')))) if session.config._environment: environment = set(session.config._environment) body.append(html.h2('Environment')) body.append(html.table( [html.tr(html.td(e[0]), html.td(e[1])) for e in sorted( environment, key=lambda e: e[0]) if e[1]], id='environment')) body.extend(summary) body.extend(results) doc = html.html(head, body) logfile.write('<!DOCTYPE html>') unicode_doc = doc.unicode(indent=2) if PY3: # Fix encoding issues, e.g. with surrogates unicode_doc = unicode_doc.encode('utf-8', errors='xmlcharrefreplace') unicode_doc = unicode_doc.decode('utf-8') logfile.write(unicode_doc) logfile.close()