def tabelize(value): try: rows = [] for key in value.keys(): rows.append(html.tr(html.td(html.pre(key)), html.td(tabelize(value[key])))) return html.table(rows) except AttributeError: if type(value) == type([]): return html.table(map(tabelize, value)) else: return html.pre(value)
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 tabelize(value): try: rows = [] for key in value.keys(): rows.append( html.tr(html.td(html.pre(key)), html.td(tabelize(value[key])))) return html.table(rows) except AttributeError: if type(value) == type([]): return html.table(map(tabelize, value)) else: return html.pre(value)
def _generate_environment(self, environment_details): rows = [] keys = [ k for k in environment_details.keys() if environment_details[k] ] if not isinstance(environment_details, OrderedDict): keys.sort() for key in keys: value = environment_details[key] if isinstance(value, basestring) and value.startswith("http"): value = html.a(value, href=value, target="_blank") elif isinstance(value, (list, tuple, set)): value = ", ".join((str(i) for i in value)) rows.append(html.tr(html.td(key), html.td(value))) environment = html.div( html.h2("Environment"), html.div( html.table(rows, id="environment"), class_="environment-info", ), class_="environment-details", ) return environment
def _generate_environment(self, config): if not hasattr(config, "_metadata") or config._metadata is None: return [] metadata = config._metadata if 'Capabilities' in metadata: metadata.pop('Capabilities') if 'Base URL' in config._metadata: metadata.pop('Base URL') environment = [html.h2("Environment")] rows = [] keys = [k for k in metadata.keys()] if not isinstance(metadata, OrderedDict): keys.sort() for key in keys: value = metadata[key] if isinstance(value, str) and value.startswith("http"): value = html.a(value, href=value, target="_blank") elif isinstance(value, (list, tuple, set)): value = ", ".join(str(i) for i in value) rows.append(html.tr(html.td(key), html.td(value))) environment.append(html.table(rows, id="environment")) return environment
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 version_get(self, user, index, name, version): stage = self.getstage(user, index) name = ensure_unicode(name) version = ensure_unicode(version) metadata = stage.get_projectconfig(name) if not metadata: abort(404, "project %r does not exist" % name) verdata = metadata.get(version, None) if not verdata: abort(404, "version %r does not exist" % version) if json_preferred(): apireturn(200, type="versiondata", result=verdata) # if html show description and metadata rows = [] for key, value in sorted(verdata.items()): if key == "description": continue if isinstance(value, list): value = html.ul([html.li(x) for x in value]) rows.append(html.tr(html.td(key), html.td(value))) title = "%s/: %s-%s metadata and description" % ( stage.name, name, version) content = stage.get_description(name, version) #css = "https://pypi.python.org/styles/styles.css" return simple_html_body(title, [html.table(*rows), py.xml.raw(content)], extrahead= [html.link(media="screen", type="text/css", rel="stylesheet", title="text", href="https://pypi.python.org/styles/styles.css")] ).unicode(indent=2)
def version_get(self, user, index, name, version): stage = self.getstage(user, index) name = ensure_unicode(name) version = ensure_unicode(version) metadata = stage.get_projectconfig(name) if not metadata: abort(404, "project %r does not exist" % name) verdata = metadata.get(version, None) if not verdata: abort(404, "version %r does not exist" % version) if json_preferred(): apireturn(200, type="versiondata", result=verdata) # if html show description and metadata rows = [] for key, value in sorted(verdata.items()): if key == "description": continue if isinstance(value, list): value = html.ul([html.li(x) for x in value]) rows.append(html.tr(html.td(key), html.td(value))) title = "%s/: %s-%s metadata and description" % (stage.name, name, version) content = stage.get_description(name, version) #css = "https://pypi.python.org/styles/styles.css" return simple_html_body( title, [html.table(*rows), py.xml.raw(content)], extrahead=[ html.link(media="screen", type="text/css", rel="stylesheet", title="text", href="https://pypi.python.org/styles/styles.css") ]).unicode(indent=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 _generate_environment(self, config): if not hasattr(config, "_metadata") or config._metadata is None: return [] metadata = config._metadata environment = [html.h2("Environment")] rows = [] keys = [k for k in metadata.keys()] if not isinstance(metadata, OrderedDict): keys.sort() for key in keys: value = metadata[key] if isinstance(value, str) and value.startswith("http"): value = html.a(value, href=value, target="_blank") elif isinstance(value, (list, tuple, set)): value = ", ".join(str(i) for i in sorted(map(str, value))) elif isinstance(value, dict): key_value_list = [f"'{k}': {value[k]}" for k in sorted(value)] value = ", ".join(key_value_list) value = "{" + value + "}" raw_value_string = raw(str(value)) rows.append(html.tr(html.td(key), html.td(raw_value_string))) environment.append(html.table(rows, id="environment")) return environment
def _generate_environment(self, config): if not hasattr(config, "_metadata") or config._metadata is None: return [] metadata = config._metadata environment = [html.h2("测试环境")] rows = [] keys = [k for k in metadata.keys()] # if not isinstance(metadata, OrderedDict): # keys.sort() for key in keys: value = metadata[key] if isinstance(value, str) and value.startswith("http"): value = html.a(value, href=value, target="_blank") elif isinstance(value, (list, tuple, set)): value = ", ".join(str(i) for i in sorted(map(str, value))) elif isinstance(value, dict): sorted_dict = {k: value[k] for k in sorted(value)} value = json.dumps(sorted_dict) raw_value_string = raw(str(value)) rows.append(html.tr(html.td(TEST_ENV.get(key)), html.td(raw_value_string))) environment.append(html.table(rows, id="environment")) return environment
def make_errors_table(self, errors): rows = [] for error in errors: rows.append(html.tr( html.td(error["level"], class_="log_%s" % error["level"]), html.td(error.get("message", "")) )) return html.table(rows, id_="errors")
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_body(self): body_parts = [ html.div( html.script(raw( pkg_resources.resource_string( rcname, os.path.sep.join( ['resources', 'htmlreport', 'jquery.js']))), type='text/javascript'), html.script(raw( pkg_resources.resource_string( rcname, os.path.sep.join( ['resources', 'htmlreport', 'main.js']))), type='text/javascript'), html.h1("FirefoxOS Certification Suite Report"), html.p("Run at %s" % self.time.strftime("%Y-%m-%d %H:%M:%S"))) ] if self.logs: device_profile_object = None with open(self.logs[-1]) as f: device_profile_object = json.load(f)['result']['contact'] device_profile = [html.h2('Device Information')] device_table = html.table() for key in device_profile_object: device_table.append( html.tr(html.td(key), html.td(device_profile_object[key]))) #device_profile.append(html.p("%s, %s"% (key, device_profile_object[key]))) device_profile.append(device_table) body_parts.extend(device_profile) if self.summary_results.has_errors: body_parts.append(html.h2("Errors During Run")) body_parts.append( self.make_errors_table(self.summary_results.errors)) body_parts.append(html.h2("Test Results")) body_parts.append(self.make_result_table()) if self.logs: ulbody = [] for log_path in self.logs: details_log = '' with open(log_path, 'r') as f: details_log = f.read() href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode( details_log) ulbody.append( html.li( html.a(os.path.basename(log_path), href=href, target='_blank'))) device_profile_object = None body_parts.append(html.h2("Details log information")) body_parts.append(html.ul(ulbody)) return html.body(body_parts)
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_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.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 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 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 _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 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"), html.th("Subtest"), html.th("Expected"), html.th("Result"), html.th("Message") ) ), html.tbody( *self.make_table_rows() ) )
def make_body(self): body_parts = [html.div( html.script(raw(pkg_resources.resource_string( rcname, os.path.sep.join(['resources', 'htmlreport', 'jquery.js']))), type='text/javascript'), html.script(raw(pkg_resources.resource_string( rcname, os.path.sep.join(['resources', 'htmlreport', 'main.js']))), type='text/javascript'), html.h1("FirefoxOS Certification Suite Report"), html.p("Run at %s" % self.time.strftime("%Y-%m-%d %H:%M:%S")) )] if self.logs: device_profile_object = None with open(self.logs[-1]) as f: device_profile_object = json.load(f)['result']['contact'] device_profile = [html.h2('Device Information')] device_table = html.table() for key in device_profile_object: device_table.append( html.tr( html.td(key), html.td(device_profile_object[key]) ) ) #device_profile.append(html.p("%s, %s"% (key, device_profile_object[key]))) device_profile.append(device_table) body_parts.extend(device_profile); if self.summary_results.has_errors: body_parts.append(html.h2("Errors During Run")) body_parts.append(self.make_errors_table(self.summary_results.errors)) body_parts.append(html.h2("Test Results")) body_parts.append(self.make_result_table()) if self.logs: ulbody = []; for log_path in self.logs: details_log = '' with open(log_path, 'r') as f: details_log = f.read() href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(details_log) ulbody.append(html.li(html.a(os.path.basename(log_path), href=href, target='_blank'))) device_profile_object = None body_parts.append(html.h2("Details log information")) body_parts.append(html.ul(ulbody)) return html.body(body_parts)
def _generate_environment(self, config): if not hasattr(config, '_metadata') or config._metadata is None: return [] metadata = config._metadata environment = [html.h2('Environment')] rows = [] for key in [k for k in sorted(metadata.keys()) if metadata[k]]: value = metadata[key] if isinstance(value, basestring) and value.startswith('http'): value = html.a(value, href=value, target='_blank') rows.append(html.tr(html.td(key), html.td(value))) environment.append(html.table(rows, id='environment')) return environment
def create_dir_html(path, href_prefix=''): h = html.html( html.head( html.title('directory listing of %s' % (path,)), style, ), ) body = html.body( html.h1('directory listing of %s' % (path,)), ) h.append(body) table = html.table() body.append(table) tbody = html.tbody() table.append(tbody) items = list(path.listdir()) items.sort(key=lambda p: p.basename) items.sort(key=lambda p: not p.check(dir=True)) for fpath in items: tr = html.tr() tbody.append(tr) td1 = html.td(fpath.check(dir=True) and 'D' or 'F', class_='type') tr.append(td1) href = fpath.basename if href_prefix: href = '%s%s' % (href_prefix, href) if fpath.check(dir=True): href += '/' td2 = html.td(html.a(fpath.basename, href=href), class_='name') tr.append(td2) td3 = html.td(time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(fpath.mtime())), class_='mtime') tr.append(td3) if fpath.check(dir=True): size = '' unit = '' else: size = fpath.size() unit = 'B' for u in ['kB', 'MB', 'GB', 'TB']: if size > 1024: size = round(size / 1024.0, 2) unit = u td4 = html.td('%s %s' % (size, unit), class_='size') tr.append(td4) return unicode(h)
def make_errors_table(self, errors): rows = [] for error in errors: error_message = error.get("message", "") log = html.div(class_='log') for line in error_message.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()) rows.append(html.tr( html.td(error["level"], class_="log_%s" % error["level"]), html.td(log, class_='log') )) return html.table(rows, id_="errors")
def _generate_environment(self, config): if not hasattr(config, '_metadata') or config._metadata is None: return [] metadata = config._metadata environment = [html.h2('Environment')] rows = [] keys = [k for k in metadata.keys() if metadata[k]] if not isinstance(metadata, OrderedDict): keys.sort() for key in keys: value = metadata[key] if isinstance(value, basestring) and value.startswith('http'): value = html.a(value, href=value, target='_blank') elif isinstance(value, (list, tuple, set)): value = ', '.join((str(i) for i in value)) rows.append(html.tr(html.td(key), html.td(value))) environment.append(html.table(rows, id='environment')) return environment
def _generate_environment(self, config): if not hasattr(config, '_metadata') or config._metadata is None: return [] metadata = config._metadata environment = [html.h2('Environment')] rows = [] keys = [k for k in metadata.keys()] if not isinstance(metadata, OrderedDict): keys.sort() for key in keys: value = metadata[key] if isinstance(value, basestring) and value.startswith('http'): value = html.a(value, href=value, target='_blank') elif isinstance(value, (list, tuple, set)): value = ', '.join((str(i) for i in value)) rows.append(html.tr(html.td(key), html.td(value))) environment.append(html.table(rows, id='environment')) return environment
def make_errors_table(self, errors): rows = [] for error in errors: error_message = error.get("message", "") log = html.div(class_='log') for line in error_message.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()) rows.append( html.tr( html.td(error["level"], class_="log_%s" % error["level"]), html.td(log, class_='log'))) return html.table(rows, id_="errors")
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 _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_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_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)
# Generate HTML doc doc = html.html( html.head(html.meta(charset='utf-8'), html.title('Test Report'), html.link(rel='stylesheet', href='html/style.css'), html.script(src='html/jquery.js'), html.script(src='html/main.js')), html.body( html.p( 'Report generated on %s at %s by ASM %s' % (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'),
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_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()
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 indexroot(self, user, index): stage = self.getstage(user, index) if json_preferred(): projectlist = stage.getprojectnames_perstage() projectlist = sorted(projectlist) apireturn(200, type="list:projectconfig", result=projectlist) if stage.name == "root/pypi": return simple_html_body("%s index" % stage.name, [ html.ul( html.li(html.a("simple index", href="+simple/")), ), ]).unicode() # XXX this should go to a template if hasattr(stage, "ixconfig"): bases = html.ul() for base in stage.ixconfig["bases"]: bases.append(html.li( html.a("%s" % base, href="/%s/" % base), " (", html.a("simple", href="/%s/+simple/" % base), " )", )) if bases: bases = [html.h2("inherited bases"), bases] else: bases = [] latest_packages = html.table( html.tr(html.td("info"), html.td("file"), html.td("docs"))) for projectname in stage.getprojectnames_perstage(): metadata = stage.get_metadata_latest(projectname) try: name, ver = metadata["name"], metadata["version"] except KeyError: log.error("metadata for project %r empty: %s, skipping", projectname, metadata) continue dockey = stage._doc_key(name, ver) if dockey.exists(): docs = [html.a("%s-%s docs" %(name, ver), href="%s/%s/+doc/index.html" %(name, ver))] else: docs = [] files = metadata.get("+files", {}) if not files: log.warn("project %r version %r has no files", projectname, metadata.get("version")) baseurl = URL(request.path) for basename, relpath in files.items(): latest_packages.append(html.tr( html.td(html.a("%s-%s info page" % (name, ver), href="%s/%s/" % (name, ver))), html.td(html.a(basename, href=baseurl.relpath("/" + relpath))), html.td(*docs), )) break # could present more releasefiles latest_packages = [ html.h2("in-stage latest packages, at least as recent as bases"), latest_packages] return simple_html_body("%s index" % stage.name, [ html.ul( html.li(html.a("simple index", href="+simple/")), ), latest_packages, bases, ]).unicode()
def indexroot(self, user, index): stage = self.getstage(user, index) if json_preferred(): projectlist = stage.getprojectnames_perstage() projectlist = sorted(projectlist) apireturn(200, type="list:projectconfig", result=projectlist) if stage.name == "root/pypi": return simple_html_body("%s index" % stage.name, [ html.ul(html.li(html.a("simple index", href="+simple/")), ), ]).unicode() # XXX this should go to a template if hasattr(stage, "ixconfig"): bases = html.ul() for base in stage.ixconfig["bases"]: bases.append( html.li( html.a("%s" % base, href="/%s/" % base), " (", html.a("simple", href="/%s/+simple/" % base), " )", )) if bases: bases = [html.h2("inherited bases"), bases] else: bases = [] latest_packages = html.table( html.tr(html.td("info"), html.td("file"), html.td("docs"))) for projectname in stage.getprojectnames_perstage(): metadata = stage.get_metadata_latest(projectname) try: name, ver = metadata["name"], metadata["version"] except KeyError: log.error("metadata for project %r empty: %s, skipping", projectname, metadata) continue dockey = stage._doc_key(name, ver) if dockey.exists(): docs = [ html.a("%s-%s docs" % (name, ver), href="%s/%s/+doc/index.html" % (name, ver)) ] else: docs = [] files = metadata.get("+files", {}) if not files: log.warn("project %r version %r has no files", projectname, metadata.get("version")) baseurl = URL(request.path) for basename, relpath in files.items(): latest_packages.append( html.tr( html.td( html.a("%s-%s info page" % (name, ver), href="%s/%s/" % (name, ver))), html.td( html.a(basename, href=baseurl.relpath("/" + relpath))), html.td(*docs), )) break # could present more releasefiles latest_packages = [ html.h2("in-stage latest packages, at least as recent as bases"), latest_packages ] return simple_html_body("%s index" % stage.name, [ html.ul(html.li(html.a("simple index", href="+simple/")), ), latest_packages, bases, ]).unicode()
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()
# Generate HTML doc doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.link(rel='stylesheet', href='html/style.css'), html.script(src='html/jquery.js'), html.script(src='html/main.js')), html.body( html.p('Report generated on %s at %s by ASM %s' % ( 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([
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 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 to BrowserStack') ]), 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()
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_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() css_path = Path(__file__).parent / "resources" / "style.css" self.style_css = css_path.read_text() if ansi_support(): ansi_css = [ "\n/******************************", " * ANSI2HTML STYLES", " ******************************/\n", ] ansi_css.extend( [str(r) for r in ansi_support().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" self.style_css += Path(path).read_text() 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)) session.config.hook.pytest_html_report_title(report=self) head = html.head(html.meta(charset="utf-8"), html.title(self.title), html_css) 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", col="duration"), html.th("Links", class_="sortable links", col="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_path = Path(__file__).parent / "resources" / "main.js" main_js = main_js_path.read_text() body = html.body( html.script(raw(main_js)), html.h1(self.title), html.p( "Report generated on {} at {} by ".format( generated.strftime("%d-%b-%Y"), 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 test_duration = '%.4f' %(suite_time_delta) numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() jkbuildid=session.config.getoption("--jkbuildid") jkjobname=session.config.getoption("--jkjobname") if jkbuildid!=-1 and jkjobname: #print('update test collection to mysql, jobname: {}, buildid: {}'.format(jkjobname,jkbuildid)) try: from localplugins.mysql_opr import query_pymysql fpath=os.path.join(BASE_DIR,'localplugins','resources', 'mysql_conn.json') #print('fpath :',fpath) f=open(fpath, 'r', encoding='utf-8') dbinfo = json.loads(f.read()) sql=''' UPDATE uitest_collect SET fail_total={},pass_total={},skip_total={},error_total={},run_total={},duration='{}' WHERE jk_jobname='{}' AND jk_buildid='{}' '''.format(self.failed,self.passed,self.skipped,self.errors,numtests,test_duration,jkjobname,jkbuildid) #print('update uitest_collect: ',sql) query_pymysql(dbinfo['host'],dbinfo['user'],dbinfo['password'],dbinfo['port'],'qateam',sql) #print('.......update test collection to mysql <uitest_collect>......',time.strftime('%Y-%m-%d %H:%M:%S')) except Exception as e: print('[Exception<updating to uitest_collect>]',end='') #print('Exception when updating test collection to mysql: ',e) self.style_css = pkg_resources.resource_string( __name__, os.path.join('resources', 'style.css')) #print() #print('sytle_css path: ',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')] ''' 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( '{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')] ''' summary_thead = [ html.th('失败'), html.th('总共'), html.th('成功'), html.th('过滤'), html.th('报错')] summary_tbody = [ html.td(self.failed,class_='red'), html.td(numtests), html.td(self.passed), html.td(self.skipped), html.td(self.errors,style='color:red;')] # summary_table = [ # html.table([ # html.thead( # html.tr(summary_thead), # id='results-table-head' # ), # html.tbody(html.tr(summary_tbody)) # ], # class_='table-50') # ] summary = [ html.p('总共 {0} 个用例,总耗时: {1:.2f} 秒. '.format( numtests, suite_time_delta)), html.table([ html.thead( html.tr(summary_thead), id='results-summary' ), html.tbody(html.tr(summary_tbody)) ], class_='table-350') ] ''' summary = [ html.p('总共 {0} 个用例,总耗时: {1:.2f} 秒. '.format( numtests, suite_time_delta)), html.table([ html.thead( html.tr(summary_thead), id='results-summary' ), html.tbody(html.tr(summary_tbody)) ], class_='table-50'), 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('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') ] ''' cells = [ html.th('用例状态', class_='sortable result initial-sort', col='result'), html.th('用例功能描述', col='description'), html.th('测试用例', class_='sortable', col='name'), html.th('运行耗时', class_='sortable numeric', col='duration') #,html.th('链接') ] 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')] ''' results = [ html.h2('测试结果'), 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',class_='table') ] #print('self.test_logs: ',self.test_logs) main_js = pkg_resources.resource_string( __name__, os.path.join('resources', 'main.js')) if PY3: main_js = main_js.decode('utf-8') '''***********************************gql*************************************** 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__)), # html 标题 ''' body = html.body( html.script(raw(main_js)), #html.h1(os.path.basename(session.config.option.htmlpath)), html.h1(self.htmlhead), html.p('测试报告运行于: {0} {1}'.format( generated.strftime('%Y-%m-%d'), generated.strftime('%H:%M:%S')) #,html.a('pytest-html', href='http://www.baidu.com/'),' v{0}'.format('2.11') ), onLoad='init()') '''***********************************gql*************************************** body.extend(self._generate_environment(session.config)) ''' #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) '''***********************************gql*************************************** body.extend([html.h2('Summary')] + summary_prefix + summary + summary_postfix) ''' #print('html-body-extend--summary: ',summary) 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
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 = \ """ body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; /* do not increase min-width as some may use split screens */ min-width: 800px; color: #999; } h1 { font-size: 24px; color: black; } h2 { font-size: 16px; color: black; } p { color: black; } a { color: #999; } table { border-collapse: collapse; } /****************************** * SUMMARY INFORMATION ******************************/ #environment td { padding: 5px; border: 1px solid #E6E6E6; } #environment tr:nth-child(odd) { background-color: #f6f6f6; } /****************************** * TEST RESULT COLORS ******************************/ span.passed, .passed .col-result { color: green; } span.skipped, span.xfailed, span.rerun, .skipped .col-result, .xfailed .col-result, .rerun .col-result { color: orange; } span.error, span.failed, span.xpassed, .error .col-result, .failed .col-result, .xpassed .col-result { color: red; } /****************************** * RESULTS TABLE * * 1. Table Layout * 2. Extra * 3. Sorting items * ******************************/ /*------------------ * 1. Table Layout *------------------*/ #results-table { border: 1px solid #e6e6e6; color: #999; font-size: 12px; width: 100% } #results-table th, #results-table td { padding: 5px; border: 1px solid #E6E6E6; text-align: left } #results-table th { font-weight: bold } /*------------------ * 2. Extra *------------------*/ .log:only-child { height: inherit } .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 } div.image { border: 1px solid #e6e6e6; float: right; height: 240px; margin-left: 5px; overflow: hidden; width: 320px } div.image img { width: 320px } div.video { border: 1px solid #e6e6e6; float: right; height: 240px; margin-left: 5px; overflow: hidden; width: 320px } div.video video { overflow: hidden; width: 320px; height: 240px; } .collapsed { display: none; } .expander::after { content: " (show details)"; color: #BBB; font-style: italic; cursor: pointer; } .collapser::after { content: " (hide details)"; color: #BBB; font-style: italic; cursor: pointer; } /*------------------ * 3. Sorting items *------------------*/ .sortable { cursor: pointer; } .sort-icon { font-size: 0px; float: left; margin-right: 5px; margin-top: 5px; /*triangle*/ width: 0; height: 0; border-left: 8px solid transparent; border-right: 8px solid transparent; } .inactive .sort-icon { /*finish triangle*/ border-top: 8px solid #E6E6E6; } .asc.active .sort-icon { /*finish triangle*/ border-bottom: 8px solid #999; } .desc.active .sort-icon { /*finish triangle*/ border-top: 8px solid #999; } """ 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.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 = \ """ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ function toArray(iter) { if (iter === null) { return null; } return Array.prototype.slice.call(iter); } function find(selector, elem) { if (!elem) { elem = document; } return elem.querySelector(selector); } function find_all(selector, elem) { if (!elem) { elem = document; } return toArray(elem.querySelectorAll(selector)); } function sort_column(elem) { toggle_sort_states(elem); var colIndex = toArray(elem.parentNode.childNodes).indexOf(elem); var key; if (elem.classList.contains('numeric')) { key = key_num; } else if (elem.classList.contains('result')) { key = key_result; } else { key = key_alpha; } sort_table(elem, key(colIndex)); } function show_all_extras() { find_all('.col-result').forEach(show_extras); } function hide_all_extras() { find_all('.col-result').forEach(hide_extras); } function show_extras(colresult_elem) { var extras = colresult_elem.parentNode.nextElementSibling; var expandcollapse = colresult_elem.firstElementChild; extras.classList.remove("collapsed"); expandcollapse.classList.remove("expander"); expandcollapse.classList.add("collapser"); } function hide_extras(colresult_elem) { var extras = colresult_elem.parentNode.nextElementSibling; var expandcollapse = colresult_elem.firstElementChild; extras.classList.add("collapsed"); expandcollapse.classList.remove("collapser"); expandcollapse.classList.add("expander"); } function show_filters() { var filter_items = document.getElementsByClassName('filter'); for (var i = 0; i < filter_items.length; i++) filter_items[i].hidden = false; } function add_collapse() { // Add links for show/hide all var resulttable = find('table#results-table'); var showhideall = document.createElement("p"); showhideall.innerHTML = '<a href="javascript:show_all_extras()">Show all details</a> / ' + '<a href="javascript:hide_all_extras()">Hide all details</a>'; resulttable.parentElement.insertBefore(showhideall, resulttable); // Add show/hide link to each result find_all('.col-result').forEach(function(elem) { var collapsed = get_query_parameter('collapsed') || 'Passed'; var extras = elem.parentNode.nextElementSibling; var expandcollapse = document.createElement("span"); if (extras.classList.contains("collapsed")) { expandcollapse.classList.add("expander") } else if (collapsed.includes(elem.innerHTML)) { extras.classList.add("collapsed"); expandcollapse.classList.add("expander"); } else { expandcollapse.classList.add("collapser"); } elem.appendChild(expandcollapse); elem.addEventListener("click", function(event) { if (event.currentTarget.parentNode.nextElementSibling.classList.contains("collapsed")) { show_extras(event.currentTarget); } else { hide_extras(event.currentTarget); } }); }) } function get_query_parameter(name) { var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); } function init () { reset_sort_headers(); add_collapse(); show_filters(); sort_column(find('.initial-sort')); find_all('.sortable').forEach(function(elem) { elem.addEventListener("click", function(event) { sort_column(elem); }, false) }); }; function sort_table(clicked, key_func) { var rows = find_all('.results-table-row'); var reversed = !clicked.classList.contains('asc'); var sorted_rows = sort(rows, key_func, reversed); /* Whole table is removed here because browsers acts much slower * when appending existing elements. */ var thead = document.getElementById("results-table-head"); document.getElementById('results-table').remove(); var parent = document.createElement("table"); parent.id = "results-table"; parent.appendChild(thead); sorted_rows.forEach(function(elem) { parent.appendChild(elem); }); document.getElementsByTagName("BODY")[0].appendChild(parent); } function sort(items, key_func, reversed) { var sort_array = items.map(function(item, i) { return [key_func(item), i]; }); sort_array.sort(function(a, b) { var key_a = a[0]; var key_b = b[0]; if (key_a == key_b) return 0; if (reversed) { return (key_a < key_b ? 1 : -1); } else { return (key_a > key_b ? 1 : -1); } }); return sort_array.map(function(item) { var index = item[1]; return items[index]; }); } function key_alpha(col_index) { return function(elem) { return elem.childNodes[1].childNodes[col_index].firstChild.data.toLowerCase(); }; } function key_num(col_index) { return function(elem) { return parseFloat(elem.childNodes[1].childNodes[col_index].firstChild.data); }; } function key_result(col_index) { return function(elem) { var strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed', 'Skipped', 'Passed']; return strings.indexOf(elem.childNodes[1].childNodes[col_index].firstChild.data); }; } function reset_sort_headers() { find_all('.sort-icon').forEach(function(elem) { elem.parentNode.removeChild(elem); }); find_all('.sortable').forEach(function(elem) { var icon = document.createElement("div"); icon.className = "sort-icon"; icon.textContent = "vvv"; elem.insertBefore(icon, elem.firstChild); elem.classList.remove("desc", "active"); elem.classList.add("asc", "inactive"); }); } function toggle_sort_states(elem) { //if active, toggle between asc and desc if (elem.classList.contains('active')) { elem.classList.toggle('asc'); elem.classList.toggle('desc'); } //if inactive, reset all other functions and add ascending active if (elem.classList.contains('inactive')) { reset_sort_headers(); elem.classList.remove('inactive'); elem.classList.add('active'); } } function is_all_rows_hidden(value) { return value.hidden == false; } function filter_table(elem) { var outcome_att = "data-test-result"; var outcome = elem.getAttribute(outcome_att); class_outcome = outcome + " results-table-row"; var outcome_rows = document.getElementsByClassName(class_outcome); for(var i = 0; i < outcome_rows.length; i++){ outcome_rows[i].hidden = !elem.checked; } var rows = find_all('.results-table-row').filter(is_all_rows_hidden); var all_rows_hidden = rows.length == 0 ? true : false; var not_found_message = document.getElementById("not-found-message"); not_found_message.hidden = !all_rows_hidden; } """ session.config.hook.pytest_html_report_title(report=self) body = html.body( html.script(raw(main_js)), html.h1(self.title), html.p( "Report generated on {} at {} by ".format( generated.strftime("%d-%b-%Y"), 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() 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 _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
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)
def create_table(self): table = html.table(cellpadding='0', cellspacing='0') tbody = html.tbody() table.append(tbody) return table, tbody
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 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_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.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()