def _populate_html_log_div(self, log, report): if report.longrepr: # longreprtext is only filled out on failure by pytest # otherwise will be None. # Use full_text if longreprtext is None-ish # we added full_text elsewhere in this file. text = report.longreprtext or report.full_text for line in text.splitlines(): separator = line.startswith("_ " * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append( html.span(raw(escape(line)), class_="error")) else: log.append(raw(escape(line))) log.append(html.br()) for section in report.sections: header, content = map(escape, section) log.append(f" {header:-^80} ") log.append(html.br()) if ansi_support(): converter = ansi_support().Ansi2HTMLConverter( inline=False, escaped=False) content = converter.convert(content, full=False) else: content = _remove_ansi_escape_sequences(content) log.append(raw(content)) log.append(html.br())
def pytest_html_results_table_html(report, data): if report.failed: # Extract important lines failure_lines = [ line for line in lxmlhtml.fromstring(str(data[-1])).xpath( '//div/text()') if line.startswith(">") or " def " in line ] error_spans = [ str(html.span("* " + line[1:].lstrip().rstrip(), class_='error')) for line in lxmlhtml.fromstring( str(data[-1])).xpath('//span/text()') if "Fail Message(s) for test" not in line ] # Reconstruct error log # Taken from append_log_html in https://github.com/pytest-dev/pytest-html/blob/master/pytest_html/plugin.py del data[-1] log = html.div(class_='log') for line in failure_lines: log.append(raw(line)) log.append(html.br()) for line in error_spans: log.append(html.span(raw(line), class_='error')) log.append(html.br()) data.append(log) elif report.passed: del data[1]
def append_log_html(self, report, additional_html): log = html.div(class_='log') if report.longrepr: for line in str(report.longrepr).splitlines(): if not PY3: line = line.decode('utf-8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for header, content in report.sections: log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) log.append(content) if len(log) == 0: log = html.div(class_='empty log') log.append('No log output captured.') additional_html.append(log)
def append_log_html(self, report, additional_html): log = html.div(class_='log') if report.longrepr: for line in str(report.longrepr).splitlines(): if not PY3: line = line.decode('utf-8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append( html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for section in report.sections: header, content = map(escape, section) log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) if ANSI: converter = Ansi2HTMLConverter(inline=False, escaped=False) content = converter.convert(content, full=False) log.append(raw(content)) if len(log) == 0: log = html.div(class_='empty log') log.append('No log output captured.') additional_html.append(log)
def append_log_html(self, report, additional_html): log = html.div(class_='log') if report.longrepr: for line in report.longreprtext.splitlines(): separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for section in report.sections: #header, content = map(escape, section) header = section[0] content = escape(section[1].replace("\\","\\\\")) #print('header1 : ',header) #print('content1 : ',content) log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) if ANSI: converter = Ansi2HTMLConverter(inline=False, escaped=False) content = converter.convert(content, full=False) log.append(raw(content)) if len(log) == 0: log = html.div(class_='empty log') log.append('No log output captured.') additional_html.append(log)
def append_log_html(self, report, additional_html): log = html.div(class_="log") if report.longrepr: for line in report.longreprtext.splitlines(): separator = line.startswith("_ " * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append( html.span(raw(escape(line)), class_="error")) else: log.append(raw(escape(line))) log.append(html.br()) for section in report.sections: header, content = map(escape, section) log.append(f" {header:-^80} ") log.append(html.br()) if ANSI: converter = Ansi2HTMLConverter(inline=False, escaped=False) content = converter.convert(content, full=False) log.append(raw(content)) log.append(html.br()) if len(log) == 0: log = html.div(class_="empty log") log.append("No log output captured.") additional_html.append(log)
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 append_log_html(self, report, additional_html): log = html.div(class_='log') if report.longrepr: for line in report.longreprtext.splitlines(): separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for section in report.sections: header, content = map(escape, section) log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) if ANSI: converter = Ansi2HTMLConverter(inline=False, escaped=False) content = converter.convert(content, full=False) log.append(raw(content)) if len(log) == 0: log = html.div(class_='empty log') log.append('No log output captured.') additional_html.append(log)
def generate_html(self): generated = datetime.datetime.now() with open(os.path.join(base_path, "main.js")) as main_f: doc = html.html( self.head, html.body( html.script(raw(main_f.read()), ), html.p( 'Report generated on %s at %s' % (generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S')), html.h2('Summary'), html.p( '%i tests ran in %.1f seconds.' % (sum(self.test_count.itervalues()), (self.suite_times["end"] - self.suite_times["start"]) / 1000.), html.br(), html.span('%i passed' % self.test_count["PASS"], class_='pass'), ', ', html.span('%i skipped' % self.test_count["SKIP"], class_='skip'), ', ', html.span('%i failed' % self.test_count["UNEXPECTED_FAIL"], class_='fail'), ', ', html.span('%i errors' % self.test_count["UNEXPECTED_ERROR"], class_='error'), '.', html.br(), html.span('%i expected failures' % self.test_count["EXPECTED_FAIL"], class_='expected_fail'), ', ', html.span('%i unexpected passes' % self.test_count["UNEXPECTED_PASS"], class_='unexpected_pass'), '.'), html.h2('Results'), html.table([ html.thead(html.tr([ html.th( 'Result', class_='sortable', col='result'), html.th( 'Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links') ]), id='results-table-head'), html.tbody(self.result_rows, id='results-table-body') ], id='results-table')))) return u"<!DOCTYPE html>\n" + doc.unicode(indent=2)
def 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 _append_exception_section(self, div, exc): for line in exc.splitlines(): separator = line.startswith('_ ' * 10) if separator: div.append(line[:80]) div.append(html.br()) else: exception = line.startswith("E ") if exception: div.append(html.span(raw(escape(line)), class_='error')) div.append(html.br()) else: div.append(raw(escape(line))) div.append(html.br())
def pytest_html_results_table_row(report, cells): if hasattr(report, '_batch') and report._batch: score = "-0" # unable to resovle the domain subresult_html = [] if report._score: score = report._score[0] for k in sorted(report._subresults[0].keys()): subresult_html.append(make_result_square(k, report._subresults[0][k])) subresult_html.append(html.span(f'{report._reference}', style='padding-left: 5px')) if len(report._subresults) > 1: subresult_html.append(html.br()) for k in sorted(report._subresults[0].keys()): if k in report._subresults[1]: result = report._subresults[1].pop(k) else: result = None subresult_html.append(make_result_square(k, result)) for k in sorted(report._subresults[1].keys()): subresult_html.append(make_result_square(k, report._subresults[1][k])) subresult_html.append(html.span(f'{report._demo}', style='padding-left: 5px')) score = report._score[1] - score cells[1] = html.td(sortsafe(report._fqdn)) cells.insert(2, html.td(sortsafe(score))) cells.insert(3, html.td(sortsafe(subresult_html), style='font_family:monospace')) cells.insert(4, html.td(sortsafe(br_join(report._failures)))) cells.insert(5, html.td(sortsafe(br_join(report._warnings))))
def simple_list_project(self, user, index, projectname): # we only serve absolute links so we don't care about the route's slash stage = self.getstage(user, index) result = stage.getreleaselinks(projectname) if isinstance(result, int): if result == 404: abort(404, "no such project") if result >= 500: abort(502, "upstream server has internal error") if result < 0: abort(502, "upstream server not reachable") links = [] for entry in result: href = "/pkg/" + entry.relpath if entry.eggfragment: href += "#egg=%s" % entry.eggfragment elif entry.md5: href += "#md5=%s" % entry.md5 links.append((href, entry.basename)) # construct html body = [] for entry in links: body.append(html.a(entry[1], href=entry[0])) body.append(html.br()) return simple_html_body("%s: links for %s" % (stage.name, projectname), body).unicode()
def _append_run_error(self, div, run_error): run_error_div = html.div(class_="run_error") self._append_exception_section(run_error_div, run_error) div.append(run_error_div) div.append(html.br())
def pytest_runtest_makereport(item, call): # Define HTML style pytest_html = item.config.pluginmanager.getplugin('html') pytest_html.html.body = HTMLStyle.body pytest_html.html.table = HTMLStyle.table pytest_html.html.th = HTMLStyle.th pytest_html.html.td = HTMLStyle.td pytest_html.html.h1 = HTMLStyle.h1 pytest_html.html.h2 = HTMLStyle.h2 pytest_html.html.h3 = HTMLStyle.h3 outcome = yield report = outcome.get_result() # Store the test case name report.test_name = item.spec['test_name'] # Store the test case stages report.stages = list() for stage in item.spec['stages']: report.stages.extend((stage['name'], html.br())) if report.location[0] not in results: results[report.location[0]] = {'passed': 0, 'failed': 0, 'skipped': 0, 'xfailed': 0, 'error': 0} if report.when == 'call': if report.longrepr is not None and report.longreprtext.split()[-1] == 'XFailed': results[report.location[0]]['xfailed'] += 1 else: results[report.location[0]][report.outcome] += 1 elif report.outcome == 'failed': results[report.location[0]]['error'] += 1
def simple_list_project(self, user, index, projectname): # we only serve absolute links so we don't care about the route's slash abort_if_invalid_projectname(projectname) stage = self.getstage(user, index) projectname = ensure_unicode(projectname) info = stage.get_project_info(projectname) if info and info.name != projectname: redirect("/%s/+simple/%s/" % (stage.name, info.name)) result = stage.getreleaselinks(projectname) if isinstance(result, int): if result == 404: # we don't want pip/easy_install to try the whole simple # page -- we know for sure there is no fitting project # because all devpi indexes perform package name normalization abort(200, "no such project %r" % projectname) if result >= 500: abort(502, "upstream server has internal error") if result < 0: abort(502, "upstream server not reachable") links = [] for entry in result: relpath = entry.relpath href = "/" + relpath href = URL(request.path).relpath(href) if entry.eggfragment: href += "#egg=%s" % entry.eggfragment elif entry.md5: href += "#md5=%s" % entry.md5 links.extend([ "/".join(relpath.split("/", 2)[:2]) + " ", html.a(entry.basename, href=href), html.br(), "\n", ]) return simple_html_body("%s: links for %s" % (stage.name, projectname), links).unicode(indent=2)
def _appendrow(self, result, report): time = getattr(report, 'duration', 0.0) additional_html = [] links_html = [] for extra in getattr(report, 'extra', []): href = None if extra.get('format') == extras.FORMAT_IMAGE: href = '#' image = 'data:image/png;base64,%s' % extra.get('content') additional_html.append(html.div( html.a(html.img(src=image), href="#"), class_='image')) elif extra.get('format') == extras.FORMAT_HTML: additional_html.append(extra.get('content')) elif extra.get('format') == extras.FORMAT_JSON: href = data_uri(json.dumps(extra.get('content')), mime_type='application/json') elif extra.get('format') == extras.FORMAT_TEXT: href = data_uri(extra.get('content')) elif extra.get('format') == extras.FORMAT_URL: href = extra.get('content') if href is not None: links_html.append(html.a( extra.get('name'), class_=extra.get('format'), href=href, target='_blank')) links_html.append(' ') if 'Passed' not in result: if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): if not PY3: line = line.decode('utf-8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) additional_html.append(log) self.test_logs.append(html.tr([ html.td(result, class_='col-result'), html.td(report.nodeid, class_='col-name'), html.td('%.2f' % time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='extra')], class_=result.lower() + ' results-table-row'))
def make_result_html(self, data): tc_time = (data["time"] - self.start_times.pop(data["test"])) / 1000. additional_html = [] debug = data.get("extra", {}) links_html = [] status = status_name = data["status"] expected = data.get("expected", status) if status != expected: status_name = "UNEXPECTED_" + status elif status != "PASS": status_name = "EXPECTED_" + status self.test_count[status_name] += 1 if status in ['SKIP', 'FAIL', '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(): 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.encode('utf-8')) links_html.append(html.a( name.title(), class_=name, href=href, target='_blank')) links_html.append(' ') log = html.div(class_='log') output = data.get('stack', '').splitlines() output.extend(data.get('message', '').splitlines()) for line in output: 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) self.result_rows.append( html.tr([html.td(status_name, class_='col-result'), html.td(data['test'], class_='col-name'), html.td('%.2f' % tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug')], class_=status_name.lower() + ' results-table-row'))
def br_join(iterable): # create [(a, br), (b, br)] from [a, b] and also handle the None case item_br_tuples = [(item, html.br()) for item in iterable or list()] # flatten [(a, br), (b, br)] to [a, br, b, br] joined = [item for sublist in item_br_tuples for item in sublist] # discard final br, if any joined and joined.pop() return joined
def simple_list_all(self, user, index): stage = self.getstage(user, index) names = stage.getprojectnames() body = [] for name in names: body.append(html.a(name, href=name + "/")) body.append(html.br()) return simple_html_body("%s: list of accessed projects" % stage.name, body).unicode()
def simple_list_project(self): request = self.request name = self.context.name # we only serve absolute links so we don't care about the route's slash abort_if_invalid_projectname(request, name) stage = self.context.stage if stage.get_projectname(name) is None: # we return 200 instead of !=200 so that pip/easy_install don't # ask for the full simple page although we know it doesn't exist # XXX change that when pip-6.0 is released? abort(request, 200, "no such project %r" % name) projectname = self.context.projectname try: result = stage.get_releaselinks(projectname) except stage.UpstreamError as e: threadlog.error(e.msg) abort(request, 502, e.msg) links = [] for link in result: relpath = link.entrypath href = "/" + relpath href = URL(request.path_info).relpath(href) if link.eggfragment: href += "#egg=%s" % link.eggfragment elif link.hash_spec: href += "#" + link.hash_spec links.extend([ "/".join(relpath.split("/", 2)[:2]) + " ", html.a(link.basename, href=href), html.br(), "\n", ]) title = "%s: links for %s" % (stage.name, projectname) if stage.has_pypi_base(projectname): refresh_title = "Refresh" if stage.ixconfig["type"] == "mirror" else \ "Refresh PyPI links" refresh_url = request.route_url( "/{user}/{index}/+simple/{name}/refresh", user=self.context.username, index=self.context.index, name=projectname) refresh_form = [ html.form( html.input( type="submit", value=refresh_title, name="refresh"), action=refresh_url, method="post"), "\n"] else: refresh_form = [] return Response(html.html( html.head( html.title(title)), html.body( html.h1(title), "\n", refresh_form, links)).unicode(indent=2))
def _appendrow(self, result, report): time = getattr(report, 'duration', 0.0) additional_html = [] links_html = [] if 'Passed' not in result: for extra in getattr(report, 'extra', []): href = None if type(extra) is Image: href = '#' image = 'data:image/png;base64,%s' % extra.content additional_html.append(html.div( html.a(html.img(src=image), href="#"), class_='image')) elif type(extra) is HTML: additional_html.append(extra.content) elif type(extra) is Text: href = 'data:text/plain;charset=utf-8;base64,%s' % \ b64encode(extra.content) elif type(extra) is URL: href = extra.content if href is not None: links_html.append(html.a( extra.name, class_=extra.__class__.__name__.lower(), href=href, target='_blank')) links_html.append(' ') if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): line = line.decode('utf-8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: 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) self.test_logs.append(html.tr([ html.td(result, class_='col-result'), html.td(report.nodeid, class_='col-name'), html.td('%.2f' % time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='extra')], class_=result.lower() + ' results-table-row'))
def _append_screenshot(self, name, log): name = re.sub('[^A-Za-z0-9_.]+', '_', name) images_saved = os.path.join(self.project_root, 'screenshots') if os.path.isdir(images_saved) and len(os.listdir(images_saved)) > 0: log.append(html.h3('Screenshots')) for file in os.listdir(images_saved): image_path = os.path.join(images_saved, file) if name in image_path: self.used_screens.append(image_path) # use relative path in img src source = image_path.replace(self.project_root, '.') log.append(source) log.append(html.br()) log.append(html.img(src=source)) log.append(html.br())
def simple_list_project(self): request = self.request name = self.context.name # we only serve absolute links so we don't care about the route's slash abort_if_invalid_projectname(request, name) stage = self.context.stage projectname = stage.get_projectname(name) if projectname is None: abort(request, 200, "no such project %r" % projectname) if name != projectname: redirect("/%s/+simple/%s/" % (stage.name, projectname)) try: result = stage.get_releaselinks(projectname) except stage.UpstreamError as e: threadlog.error(e.msg) abort(request, 502, e.msg) links = [] for link in result: relpath = link.entrypath href = "/" + relpath href = URL(request.path).relpath(href) if link.eggfragment: href += "#egg=%s" % link.eggfragment elif link.md5: href += "#md5=%s" % link.md5 links.extend([ "/".join(relpath.split("/", 2)[:2]) + " ", html.a(link.basename, href=href), html.br(), "\n", ]) title = "%s: links for %s" % (stage.name, projectname) if stage.has_pypi_base(projectname): refresh_title = "Refresh" if stage.ixconfig["type"] == "mirror" else \ "Refresh PyPI links" refresh_url = request.route_url( "/{user}/{index}/+simple/{name}/refresh", user=self.context.username, index=self.context.index, name=projectname) refresh_form = [ html.form( html.input( type="submit", value=refresh_title, name="refresh"), action=refresh_url, method="post"), "\n"] else: refresh_form = [] return Response(html.html( html.head( html.title(title)), html.body( html.h1(title), "\n", refresh_form, links)).unicode(indent=2))
def simple_list_project(self): request = self.request name = self.context.name # we only serve absolute links so we don't care about the route's slash abort_if_invalid_projectname(request, name) stage = self.context.stage projectname = stage.get_projectname(name) if projectname is None: abort(request, 200, "no such project %r" % projectname) if name != projectname: redirect("/%s/+simple/%s/" % (stage.name, projectname)) try: result = stage.get_releaselinks(projectname) except stage.UpstreamError as e: threadlog.error(e.msg) abort(request, 502, e.msg) links = [] for link in result: relpath = link.entrypath href = "/" + relpath href = URL(request.path).relpath(href) if link.eggfragment: href += "#egg=%s" % link.eggfragment elif link.md5: href += "#md5=%s" % link.md5 links.extend([ "/".join(relpath.split("/", 2)[:2]) + " ", html.a(link.basename, href=href), html.br(), "\n", ]) title = "%s: links for %s" % (stage.name, projectname) if stage.has_pypi_base(projectname): refresh_title = "Refresh" if stage.ixconfig["type"] == "mirror" else \ "Refresh PyPI links" refresh_url = request.route_url( "/{user}/{index}/+simple/{name}/refresh", user=self.context.username, index=self.context.index, name=projectname) refresh_form = [ html.form(html.input(type="submit", value=refresh_title, name="refresh"), action=refresh_url, method="post"), "\n" ] else: refresh_form = [] return Response( html.html(html.head(html.title(title)), html.body(html.h1(title), "\n", refresh_form, links)).unicode(indent=2))
def _append_crash_message_section(log, report): try: message = report.longrepr.reprcrash.message log.append(html.h3('Crash Message')) crash_message_p = html.p(class_='crash_message') for line in message.splitlines(): crash_message_p.append(escape(line)) crash_message_p.append(html.br()) log.append(crash_message_p) except: return
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'))
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", ) )
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 _appendrow(self, result, report): names = mangle_testnames(report.nodeid.split("::")) testclass = (names[:-1]) if self.prefix: testclass.insert(0, self.prefix) testclass = ".".join(testclass) testmethod = names[-1] time = getattr(report, 'duration', 0.0) additional_html = [] if not 'Passed' in result: if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): line = line.decode('utf8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append( html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) if not os.path.exists(self.screenshot_path): os.makedirs(self.screenshot_path) self.append_screenshot(testmethod, log) additional_html.append(log) output = self._write_captured_output(report) info = output.split(" ") links_html = [] for i in range(0, len(info)): if ("http" in info[i] and "browserstack.com" in info[i]): links_html.append(html.a("link", href=info[i], target='_blank')) links_html.append(' ') self.test_logs.append( html.tr([ html.td(result, class_='col-result'), html.td(testclass, class_='col-class'), html.td(testmethod, class_='col-name'), html.td(round(time), class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug') ], class_=result.lower() + ' results-table-row'))
def _get_log_from_report(self, report): log = html.div(class_='log') if report.longrepr: for line in report.longreprtext.splitlines(): separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for section in report.sections: header, content = map(escape, section) log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) if ANSI: converter = Ansi2HTMLConverter(inline=False, escaped=False) content = converter.convert(content, full=False) log.append(raw(content)) if len(log) == 0: log = html.div(class_='empty log') log.append('No log output captured.') unicode_log = log.unicode(indent=2) if PY3: # Fix encoding issues, e.g. with surrogates unicode_log = unicode_log.encode('utf-8', errors='xmlcharrefreplace') unicode_log = unicode_log.decode('utf-8') return unicode_log
def _append_stacktrace_section(log, report): log.append(html.h3('Stacktrace')) stacktrace_p = html.p(class_='stacktrace') for line in str(report.longrepr).splitlines(): separator = line.startswith('_ ' * 10) if separator: stacktrace_p.append(line[:80]) else: exception = line.startswith("E ") if exception: stacktrace_p.append(html.span(raw(escape(line)), class_='error')) else: stacktrace_p.append(raw(escape(line))) stacktrace_p.append(html.br()) log.append(stacktrace_p)
def _appendrow(self, result, report): names = mangle_testnames(report.nodeid.split("::")) testclass = (names[:-1]) if self.prefix: testclass.insert(0, self.prefix) testclass = ".".join(testclass) testmethod = names[-1] time = getattr(report, 'duration', 0.0) additional_html = [] if not 'Passed' in result: if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): line = line.decode('utf8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) if not os.path.exists(self.screenshot_path): os.makedirs(self.screenshot_path) self.append_screenshot(testmethod, log) additional_html.append(log) output = self._write_captured_output(report) info = output.split(" ") links_html = [] for i in range(0, len(info)): if ("http" in info[i] and "browserstack.com" in info[i]): links_html.append(html.a("link", href=info[i], target='_blank')) links_html.append(' ') self.test_logs.append(html.tr([ html.td(result, class_='col-result'), html.td(testclass, class_='col-class'), html.td(testmethod, class_='col-name'), html.td(round(time), class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug')], class_=result.lower() + ' results-table-row'))
def pytest_runtest_makereport(item, call): # Define HTML style pytest_html = item.config.pluginmanager.getplugin('html') pytest_html.html.body = HTMLStyle.body pytest_html.html.table = HTMLStyle.table pytest_html.html.th = HTMLStyle.th pytest_html.html.td = HTMLStyle.td pytest_html.html.h1 = HTMLStyle.h1 pytest_html.html.h2 = HTMLStyle.h2 pytest_html.html.h3 = HTMLStyle.h3 outcome = yield report = outcome.get_result() # Store the test case name report.test_name = item.spec['test_name'] # Store the test case stages report.stages = list() for stage in item.spec['stages']: report.stages.extend((stage['name'], html.br())) if report.location[0] not in results: results[report.location[0]] = { 'passed': 0, 'failed': 0, 'skipped': 0, 'xfailed': 0, 'error': 0 } if report.when == 'call': if report.longrepr is not None and report.longreprtext.split( )[-1] == 'XFailed': results[report.location[0]]['xfailed'] += 1 else: results[report.location[0]][report.outcome] += 1 elif report.outcome == 'failed': results[report.location[0]]['error'] += 1 if report.when == 'setup' and \ report.longrepr and ('api_test did not yield a value' in report.longrepr.reprcrash.message or 'StopIteration' in report.longrepr.reprcrash.message): report.sections.append(('Environment section', environment_status))
def findlinks_view(context, request): title = "%s: all package links without root/pypi" % (context.stage.name) projectnames = set() for stage, names in context.stage.op_sro("list_projectnames_perstage"): if stage.ixconfig["type"] == "mirror": continue projectnames.update(names) all_links = [] basenames = set() for projectname in sorted(projectnames): for stage, res in context.stage.op_sro_check_pypi_whitelist( "get_releaselinks_perstage", projectname=projectname): if stage.ixconfig["type"] == "mirror": continue for link in res: if link.eggfragment: key = link.eggfragment else: key = link.basename if key not in basenames: basenames.add(key) all_links.append(link) links = [] for link in sorted(all_links, key=attrgetter('basename')): href = url_for_entrypath(request, link.entrypath) entry = link.entry if entry.eggfragment: href += "#egg=%s" % entry.eggfragment elif entry.md5: href += "#md5=%s" % entry.md5 links.extend([ "/".join(link.entrypath.split("/", 2)[:2]) + " ", html.a(link.basename, href=href), html.br(), "\n"]) if not links: links = [html.p('No releases.')] return Response(html.html( html.head( html.title(title)), html.body( html.h1(title), "\n", links)).unicode(indent=2))
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 findlinks_view(context, request): title = "%s: all package links without root/pypi" % (context.stage.name) projects = set() for stage, names in context.stage.op_sro("list_projects_perstage"): if stage.ixconfig["type"] == "mirror": continue projects.update(names) all_links = [] basenames = set() for project in sorted(projects): for stage, res in context.stage.op_sro_check_mirror_whitelist( "get_releaselinks_perstage", project=project): if stage.ixconfig["type"] == "mirror": continue for link in res: if getattr(link, 'eggfragment', None): key = link.eggfragment else: key = link.basename if key not in basenames: basenames.add(key) all_links.append(link) links = [] for link in sorted(all_links, key=attrgetter('basename')): href = url_for_entrypath(request, link.entrypath) entry = link.entry if getattr(entry, 'eggfragment', None): href += "#egg=%s" % entry.eggfragment elif entry.hash_spec: href += "#%s" % entry.hash_spec links.extend([ "/".join(link.entrypath.split("/", 2)[:2]) + " ", html.a(link.basename, href=href), html.br(), "\n" ]) if not links: links = [html.p('No releases.')] return Response( html.html(html.head(html.title(title)), html.body(html.h1(title), "\n", links)).unicode(indent=2))
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 _appendrow(self, result, report): time = getattr(report, "duration", 0.0) additional_html = [] # links_html = [] for extra in getattr(report, "extra", []): href = None if extra.get("format") == extras.FORMAT_IMAGE: href = "#" image = "%s" % extra.get("content") if os.path.isfile(image): with open(image, "rb") as image_file: image_file = b64encode(image_file.read()) image = "data:image/png;base64,%s" % image_file else: image = "data:image/png;base64,%s" % image additional_html.append(html.div(html.a(html.img(src=image), href="#"), class_="image")) elif extra.get("format") == extras.FORMAT_HTML: additional_html.append(extra.get("content")) elif extra.get("format") == extras.FORMAT_JSON: href = data_uri(json.dumps(extra.get("content")), mime_type="application/json") elif extra.get("format") == extras.FORMAT_TEXT: href = data_uri(extra.get("content")) elif extra.get("format") == extras.FORMAT_URL: href = extra.get("content") # if href is not None: # links_html.append(html.a( # extra.get('name'), # class_=extra.get('format'), # href=href, # target='_blank')) # links_html.append(' ') if "Passed" not in result: if report.longrepr: log = html.div(class_="log") for line in str(report.longrepr).splitlines(): if not PY3: line = line.decode("utf-8") separator = line.startswith("_ " * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_="error")) else: log.append(raw(escape(line))) log.append(html.br()) additional_html.append(log) self.test_logs.append( html.tr( [ html.td(result, class_="col-result"), html.td(report.nodeid, class_="col-name"), html.td("%.2f" % time, class_="col-duration"), # html.td(links_html, class_='col-links'), html.td(additional_html, class_="extra"), ], class_=result.lower() + " results-table-row", ) )
def generate_html(self, results_list): tests = sum([results.testsRun for results in results_list]) failures = sum([len(results.failures) for results in results_list]) expected_failures = sum([len(results.expectedFailures) for results in results_list]) skips = sum([len(results.skipped) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = sum([results.passed for results in results_list]) unexpected_passes = sum([len(results.unexpectedSuccesses) for results in results_list]) test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, class_name, duration=0, text="", result="passed", debug=None): cls_name = class_name tc_name = unicode(test) tc_time = duration additional_html = [] debug = debug or {} links_html = [] if result in ["skipped", "failure", "expected failure", "error"]: if debug.get("screenshot"): screenshot = "data:image/png;base64,%s" % debug["screenshot"] additional_html.append(html.div(html.a(html.img(src=screenshot), href="#"), class_="screenshot")) for name, content in debug.items(): try: if "screenshot" in name: href = "#" else: # use base64 to avoid that some browser (such as Firefox, Opera) # treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. href = "data:text/plain;charset=utf-8;base64,%s" % base64.b64encode(content) links_html.append(html.a(name.title(), class_=name, href=href, target="_blank")) links_html.append(" ") except: pass log = html.div(class_="log") for line in text.splitlines(): separator = line.startswith(" " * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower().find("exception") != -1: log.append(html.span(raw(cgi.escape(line)), class_="error")) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append( html.tr( [ html.td(result.title(), class_="col-result"), html.td(cls_name, class_="col-class"), html.td(tc_name, class_="col-name"), html.td(tc_time, class_="col-duration"), html.td(links_html, class_="col-links"), html.td(additional_html, class_="debug"), ], class_=result.lower() + " results-table-row", ) ) for results in results_list: for test in results.tests_passed: _extract_html(test.name, test.test_class) for result in results.skipped: _extract_html(result.name, result.test_class, text="\n".join(result.output), result="skipped") for result in results.failures: _extract_html( result.name, result.test_class, text="\n".join(result.output), result="failure", debug=result.debug ) for result in results.expectedFailures: _extract_html( result.name, result.test_class, text="\n".join(result.output), result="expected failure", debug=result.debug, ) for test in results.unexpectedSuccesses: _extract_html(test.name, test.test_class, result="unexpected pass") for result in results.errors: _extract_html( result.name, result.test_class, text="\n".join(result.output), result="error", debug=result.debug ) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset="utf-8"), html.title("Test Report"), html.style( raw( pkg_resources.resource_string(__name__, os.path.sep.join(["resources", "report", "style.css"])) ), type="text/css", ), ), html.body( html.script( raw( pkg_resources.resource_string(__name__, os.path.sep.join(["resources", "report", "jquery.js"])) ), type="text/javascript", ), html.script( raw(pkg_resources.resource_string(__name__, os.path.sep.join(["resources", "report", "main.js"]))), type="text/javascript", ), html.p( "Report generated on %s at %s by %s %s" % (generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S"), __name__, __version__) ), html.h2("Summary"), html.p( "%i tests ran in %i seconds." % (tests, test_time), html.br(), html.span("%i passed" % passes, class_="passed"), ", ", html.span("%i skipped" % skips, class_="skipped"), ", ", html.span("%i failed" % failures, class_="failed"), ", ", html.span("%i errors" % errors, class_="error"), ".", html.br(), html.span("%i expected failures" % expected_failures, class_="expected failure"), ", ", html.span("%i unexpected passes" % unexpected_passes, class_="unexpected pass"), ".", ), html.h2("Results"), html.table( [ html.thead( html.tr( [ html.th("Result", class_="sortable", col="result"), html.th("Class", class_="sortable", col="class"), html.th("Test Name", class_="sortable", col="name"), html.th("Duration", class_="sortable numeric", col="duration"), html.th("Links"), ] ), id="results-table-head", ), html.tbody(test_logs, id="results-table-body"), ], id="results-table", ), ), ) return doc.unicode(indent=2)
def pytest_sessionfinish(self, session, exitstatus, __multicall__): self._make_report_dir() logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed server = self.config.option.sauce_labs_credentials_file and \ 'Sauce Labs' or 'http://%s:%s' % (self.config.option.host, self.config.option.port) browser = self.config.option.browser_name and \ self.config.option.browser_version and \ self.config.option.platform and \ '%s %s on %s' % (str(self.config.option.browser_name).title(), self.config.option.browser_version, str(self.config.option.platform).title()) or \ self.config.option.environment or \ self.config.option.browser generated = datetime.datetime.now() configuration = { 'Base URL': self.config.option.base_url, 'Build': self.config.option.build, 'Selenium API': self.config.option.api, 'Driver': self.config.option.driver, 'Firefox Path': self.config.option.firefox_path, 'Google Chrome Path': self.config.option.chrome_path, 'Selenium Server': server, 'Browser': browser, 'Timeout': self.config.option.webqatimeout, 'Capture Network Traffic': self.config.option.capture_network, 'Credentials': self.config.option.credentials_file, 'Sauce Labs Credentials': self.config.option.sauce_labs_credentials_file } import pytest_mozwebqa doc = html.html( html.head( html.title('Test Report'), html.style( 'body {font-family: Helvetica, Arial, sans-serif; font-size: 12px}\n', 'body * {box-sizing: -moz-border-box; box-sizing: -webkit-border-box; box-sizing: border-box}\n', 'a {color: #999}\n', 'h2 {font-size: 16px}\n', 'table {border: 1px solid #e6e6e6; color: #999; font-size: 12px; border-collapse: collapse}\n', '#configuration tr:nth-child(odd) {background-color: #f6f6f6}\n', '#results {width:100%}\n', 'th, td {padding: 5px; border: 1px solid #E6E6E6; text-align: left}\n', 'th {font-weight: bold}\n', 'tr.passed, tr.skipped, tr.xfailed, tr.error, tr.failed, tr.xpassed {color: inherit}\n' 'tr.passed + tr.additional {display: none}\n', '.passed {color: green}\n', '.skipped, .xfailed {color: orange}\n', '.error, .failed, .xpassed {color: red}\n', '.log:only-child {height: inherit}\n', raw('.log {background-color: #e6e6e6; border: 1px solid #e6e6e6; color: black; display: block; font-family: "Courier New", Courier, monospace; height: 230px; overflow-y: scroll; padding: 5px; white-space:pre-wrap}\n' ), '.screenshot, .video {border: 1px solid #e6e6e6; float:right; height:240px; margin-left:5px; overflow:hidden; width:320px}\n', '.screenshot img {width: 320px}')), html.body( html.p('Report generated on %s at %s by pytest-mozwebqa %s' % (generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), pytest_mozwebqa.__version__)), html.h2('Configuration'), html.table([ html.tr(html.td(k), html.td(v)) for k, v in sorted(configuration.items()) if v ], id='configuration'), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (numtests, suite_time_delta), html.br(), html.span('%i passed' % self.passed, class_='passed'), ', ', html.span('%i skipped' % self.skipped, class_='skipped'), ', ', html.span('%i failed' % self.failed, class_='failed'), ', ', html.span('%i errors' % self.errors, class_='error'), '.', html.br(), html.span('%i expected failures' % self.xfailed, class_='skipped'), ', ', html.span('%i unexpected passes' % self.xpassed, class_='failed'), '.'), html.h2('Results'), html.table(html.tr(html.th('Result'), html.th('Class'), html.th('Name'), html.th('Duration'), html.th('Links')), *self.test_logs, id='results'))) logfile.write(doc.unicode(indent=2)) logfile.close()
log = html.div(class_='log') # Paint traceback for line in report_log.splitlines(): separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") #print line if exception: log.append(html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) # Add traceback as debug HTML section additional_html.append(log) ###################################################################################################### # Generate HTML row with all data test_logs.append( html.tr([ html.td(result, class_='col-result'), html.td(testclass, class_='col-class'), html.td(testmethod, class_='col-name'), html.td(round(time, 3), class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug') ], class_=result.lower() + ' results-table-row'))
def generate_html(self, results_list): tests = sum([results.testsRun for results in results_list]) failures = sum([len(results.failures) for results in results_list]) expected_failures = sum( [len(results.expectedFailures) for results in results_list]) skips = sum([len(results.skipped) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = sum([results.passed for results in results_list]) unexpected_passes = sum( [len(results.unexpectedSuccesses) for results in results_list]) test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, class_name, duration=0, text='', result='passed', debug=None): cls_name = class_name tc_name = unicode(test) tc_time = duration additional_html = [] debug = debug or {} links_html = [] if result in ['skipped', 'failure', 'expected failure', 'error']: if debug.get('screenshot'): screenshot = 'data:image/png;base64,%s' % debug[ 'screenshot'] additional_html.append( html.div(html.a(html.img(src=screenshot), href="#"), class_='screenshot')) for name, content in debug.items(): try: if 'screenshot' in name: href = '#' else: # use base64 to avoid that some browser (such as Firefox, Opera) # treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode( content) links_html.append( html.a(name.title(), class_=name, href=href, target='_blank')) links_html.append(' ') except: pass log = html.div(class_='log') for line in text.splitlines(): separator = line.startswith(' ' * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower( ).find("exception") != -1: log.append( html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append( html.tr([ html.td(result.title(), class_='col-result'), html.td(cls_name, class_='col-class'), html.td(tc_name, class_='col-name'), html.td(tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug') ], class_=result.lower() + ' results-table-row')) for results in results_list: for test in results.tests_passed: _extract_html(test.name, test.test_class) for result in results.skipped: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='skipped') for result in results.failures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='failure', debug=result.debug) for result in results.expectedFailures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='expected failure', debug=result.debug) for test in results.unexpectedSuccesses: _extract_html(test.name, test.test_class, result='unexpected pass') for result in results.errors: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='error', debug=result.debug) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw( pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'style.css']))), type='text/css')), html.body( html.script(raw( pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'jquery.js']))), type='text/javascript'), html.script(raw( pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'main.js']))), type='text/javascript'), html.p( 'Report generated on %s at %s by %s %s' % (generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), __name__, __version__)), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (tests, test_time), html.br(), html.span('%i passed' % passes, class_='passed'), ', ', html.span('%i skipped' % skips, class_='skipped'), ', ', html.span('%i failed' % failures, class_='failed'), ', ', html.span('%i errors' % errors, class_='error'), '.', html.br(), html.span('%i expected failures' % expected_failures, class_='expected failure'), ', ', html.span('%i unexpected passes' % unexpected_passes, class_='unexpected pass'), '.'), html.h2('Results'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links') ]), id='results-table-head'), html.tbody(test_logs, id='results-table-body') ], id='results-table'))) return doc.unicode(indent=2)
def pytest_sessionfinish(self, session): 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: body.append(html.h2('Environment')) body.append( html.table([ html.tr(html.td(e[0]), html.td(e[1])) for e in sorted(session.config._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 _appendrow(self, result, report): time = getattr(report, 'duration', 0.0) additional_html = [] links_html = [] for extra in getattr(report, 'extra', []): href = None if extra.get('format') == extras.FORMAT_IMAGE: href = '#' image = 'data:image/png;base64,{0}'.format( extra.get('content')) additional_html.append( html.div(html.a(html.img(src=image), href="#"), class_='image')) elif extra.get('format') == extras.FORMAT_HTML: additional_html.append(html.div(raw(extra.get('content')))) elif extra.get('format') == extras.FORMAT_JSON: href = data_uri(json.dumps(extra.get('content')), mime_type='application/json') elif extra.get('format') == extras.FORMAT_TEXT: href = data_uri(extra.get('content')) elif extra.get('format') == extras.FORMAT_URL: href = extra.get('content') if href is not None: links_html.append( html.a(extra.get('name'), class_=extra.get('format'), href=href, target='_blank')) links_html.append(' ') if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): if not PY3: line = line.decode('utf-8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for header, content in report.sections: log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) log.append(content) else: log = html.div(class_='empty log') log.append('No log output captured.') additional_html.append(log) self.test_logs.append( html.tr([ html.td(result, class_='col-result'), html.td(report.nodeid, class_='col-name'), html.td('{0:.2f}'.format(time), class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='extra') ], class_=result.lower() + ' results-table-row'))
def pytest_html_results_table_html(report, data): if report.outcome == 'failed': data[0].append(html.br()) data[0].append(html.img(src=report.screenshot))
def make_result_html(self, data): cls_name = "" tc_name = unicode(data["test"]) tc_time = (data["time"] - self.start_times.pop(data["test"])) / 1000. additional_html = [] debug = data.get("extra", {}) links_html = [] status = data["status"] expected = data.get("expected", status) if status != expected: if status == "PASS": status_name = "UNEXPECTED_" + status else: status_name = "EXPECTED_" + status else: status_name = status self.test_count[status_name] += 1 if status in ['SKIP', 'FAIL', '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 debug.get("stdout", "").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) self.result_rows.append( html.tr([ html.td(status_name, class_='col-result'), html.td(cls_name, class_='col-class'), html.td(tc_name, class_='col-name'), html.td("%.2f" % tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug') ], class_=status_name.lower() + ' results-table-row'))
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 _appendrow(self, result, report): time = getattr(report, 'duration', 0.0) additional_html = [] links_html = [] for extra in getattr(report, 'extra', []): href = None if extra.get('format') == extras.FORMAT_IMAGE: href = '#' image = 'data:image/png;base64,{0}'.format( extra.get('content')) additional_html.append(html.div( html.a(html.img(src=image), href="#"), class_='image')) elif extra.get('format') == extras.FORMAT_HTML: additional_html.append(html.div(raw(extra.get('content')))) elif extra.get('format') == extras.FORMAT_JSON: href = data_uri(json.dumps(extra.get('content')), mime_type='application/json') elif extra.get('format') == extras.FORMAT_TEXT: href = data_uri(extra.get('content')) elif extra.get('format') == extras.FORMAT_URL: href = extra.get('content') if href is not None: links_html.append(html.a( extra.get('name'), class_=extra.get('format'), href=href, target='_blank')) links_html.append(' ') if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): if not PY3: line = line.decode('utf-8') separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: log.append(html.span(raw(escape(line)), class_='error')) else: log.append(raw(escape(line))) log.append(html.br()) for header, content in report.sections: log.append(' {0} '.format(header).center(80, '-')) log.append(html.br()) log.append(content) else: log = html.div(class_='empty log') log.append('No log output captured.') additional_html.append(log) test_id = report.nodeid if report.when != 'call': test_id = '::'.join([report.nodeid, report.when]) self.test_logs.append(html.tr([ html.td(result, class_='col-result'), html.td(test_id, class_='col-name'), html.td('{0:.2f}'.format(time), class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='extra')], class_=result.lower() + ' results-table-row'))
def pytest_sessionfinish(self, session, exitstatus, __multicall__): self._make_report_dir() logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed server = self.config.option.sauce_labs_credentials_file and \ 'Sauce Labs' or 'http://%s:%s' % (self.config.option.host, self.config.option.port) browser = self.config.option.browser_name and \ self.config.option.browser_version and \ self.config.option.platform and \ '%s %s on %s' % (str(self.config.option.browser_name).title(), self.config.option.browser_version, str(self.config.option.platform).title()) or \ self.config.option.environment or \ self.config.option.browser generated = datetime.datetime.now() configuration = { 'Base URL': self.config.option.base_url, 'Build': self.config.option.build, 'Selenium API': self.config.option.api, 'Driver': self.config.option.driver, 'Firefox Path': self.config.option.firefox_path, 'Google Chrome Path': self.config.option.chrome_path, 'Selenium Server': server, 'Browser': browser, 'Timeout': self.config.option.webqatimeout, 'Capture Network Traffic': self.config.option.capture_network, 'Credentials': self.config.option.credentials_file, 'Sauce Labs Credentials': self.config.option.sauce_labs_credentials_file} import pytest_mozwebqa doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.link(rel='stylesheet', href='style.css'), html.script(src='jquery.js'), html.script(src='main.js')), html.body( html.p('Report generated on %s at %s by pytest-mozwebqa %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), pytest_mozwebqa.__version__)), html.h2('Configuration'), html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(configuration.items()) if v], id='configuration'), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (numtests, suite_time_delta), html.br(), html.span('%i passed' % self.passed, class_='passed'), ', ', html.span('%i skipped' % self.skipped, class_='skipped'), ', ', html.span('%i failed' % self.failed, class_='failed'), ', ', html.span('%i errors' % self.errors, class_='error'), '.', html.br(), html.span('%i expected failures' % self.xfailed, class_='skipped'), ', ', html.span('%i unexpected passes' % self.xpassed, class_='failed'), '.'), html.h2('Results'), html.table([ html.thead(html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(*self.test_logs, id='results-table-body')], id='results-table'))) logfile.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' + doc.unicode(indent=2)) logfile.close()
def generate_html(self, results_list): tests = sum([results.testsRun for results in results_list]) failures = sum([len(results.failures) for results in results_list]) expected_failures = sum([len(results.expectedFailures) for results in results_list]) skips = sum([len(results.skipped) for results in results_list]) errors = sum([len(results.errors) for results in results_list]) passes = sum([results.passed for results in results_list]) unexpected_passes = sum([len(results.unexpectedSuccesses) for results in results_list]) test_time = self.elapsedtime.total_seconds() test_logs = [] def _extract_html(test, class_name, duration=0, text='', result='passed', debug=None): cls_name = class_name tc_name = unicode(test) tc_time = duration additional_html = [] debug = debug or {} links_html = [] if result in ['skipped', 'failure', 'expected failure', 'error']: if debug.get('screenshot'): screenshot = 'data:image/png;base64,%s' % debug['screenshot'] additional_html.append(html.div( html.a(html.img(src=screenshot), href="#"), class_='screenshot')) for name, content in debug.items(): try: if 'screenshot' in name: href = '#' else: # use base64 to avoid that some browser (such as Firefox, Opera) # treats '#' as the start of another link if the data URL contains. # use 'charset=utf-8' to show special characters like Chinese. href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content) links_html.append(html.a( name.title(), class_=name, href=href, target='_blank')) links_html.append(' ') except: pass log = html.div(class_='log') for line in text.splitlines(): separator = line.startswith(' ' * 10) if separator: log.append(line[:80]) else: if line.lower().find("error") != -1 or line.lower().find("exception") != -1: log.append(html.span(raw(cgi.escape(line)), class_='error')) else: log.append(raw(cgi.escape(line))) log.append(html.br()) additional_html.append(log) test_logs.append(html.tr([ html.td(result.title(), class_='col-result'), html.td(cls_name, class_='col-class'), html.td(tc_name, class_='col-name'), html.td(tc_time, class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug')], class_=result.lower() + ' results-table-row')) for results in results_list: for test in results.tests_passed: _extract_html(test.name, test.test_class) for result in results.skipped: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='skipped') for result in results.failures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='failure', debug=result.debug) for result in results.expectedFailures: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='expected failure', debug=result.debug) for test in results.unexpectedSuccesses: _extract_html(test.name, test.test_class, result='unexpected pass') for result in results.errors: _extract_html(result.name, result.test_class, text='\n'.join(result.output), result='error', debug=result.debug) generated = datetime.datetime.now() doc = html.html( html.head( html.meta(charset='utf-8'), html.title('Test Report'), html.style(raw(pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'style.css']))), type='text/css')), html.body( html.script(raw(pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'jquery.js']))), type='text/javascript'), html.script(raw(pkg_resources.resource_string( __name__, os.path.sep.join(['resources', 'report', 'main.js']))), type='text/javascript'), html.p('Report generated on %s at %s by %s %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), __name__, __version__)), html.h2('Summary'), html.p('%i tests ran in %i seconds.' % (tests, test_time), html.br(), html.span('%i passed' % passes, class_='passed'), ', ', html.span('%i skipped' % skips, class_='skipped'), ', ', html.span('%i failed' % failures, class_='failed'), ', ', html.span('%i errors' % errors, class_='error'), '.', html.br(), html.span('%i expected failures' % expected_failures, class_='expected failure'), ', ', html.span('%i unexpected passes' % unexpected_passes, class_='unexpected pass'), '.'), html.h2('Results'), html.table([html.thead( html.tr([ html.th('Result', class_='sortable', col='result'), html.th('Class', class_='sortable', col='class'), html.th('Test Name', class_='sortable', col='name'), html.th('Duration', class_='sortable numeric', col='duration'), html.th('Links')]), id='results-table-head'), html.tbody(test_logs, id='results-table-body')], id='results-table'))) return doc.unicode(indent=2)
def pytest_sessionfinish(self): if not os.path.exists(os.path.dirname(self.logfile)): os.makedirs(os.path.dirname(self.logfile)) logfile = open(self.logfile, "w", encoding="utf-8") suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed generated = datetime.datetime.now() style_css = pkg_resources.resource_string(__name__, os.path.join("resources", "style.css")) if PY3: style_css = style_css.decode("utf-8") head = html.head(html.meta(charset="utf-8"), html.title("Test Report"), html.style(raw(style_css))) summary = [ html.h2("Summary"), html.p( "%i tests ran in %.2f seconds." % (numtests, suite_time_delta), html.br(), html.span("%i passed" % self.passed, class_="passed"), ", ", html.span("%i skipped" % self.skipped, class_="skipped"), ", ", html.span("%i failed" % self.failed, class_="failed"), ", ", html.span("%i errors" % self.errors, class_="error"), ".", html.br(), html.span("%i expected failures" % self.xfailed, class_="skipped"), ", ", html.span("%i unexpected passes" % self.xpassed, class_="failed"), ".", ), ] self._create_overview() overview = [ html.h2("Overview"), html.table( [ html.thead( html.tr( [ html.th("Test", col="name"), html.th("Setup", col="setup"), html.th("Call", col="call"), html.th("Teardown", col="teardown"), ] ), id="overview-table-head", ), html.tbody(*self.test_overview, id="overview-table-body"), ], id="overview-table", ), ] results = [ html.h2("Results"), html.table( [ html.thead( html.tr( [ html.th("Result", class_="sortable initial-sort result", col="result"), html.th("Test", class_="sortable", col="name"), html.th("Duration", class_="sortable numeric", col="duration"), # html.th('Links') ] ), id="results-table-head", ), html.tbody(*self.test_logs, id="results-table-body"), ], id="results-table", ), ] main_js = pkg_resources.resource_string(__name__, os.path.join("resources", "main.js")) if PY3: main_js = main_js.decode("utf-8") body = html.body( html.script(raw(main_js)), html.p("Report generated on %s at %s" % (generated.strftime("%d-%b-%Y"), generated.strftime("%H:%M:%S"))), ) environment = {} for e in self.environment: for k, v in e.items(): environment[k] = v if environment: body.append(html.h2("Environment")) body.append( html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(environment.items()) if v], id="environment" ) ) body.extend(summary) body.extend(overview) body.extend(results) doc = html.html(head, body) logfile.write("<!DOCTYPE html>") logfile.write(doc.unicode(indent=2)) logfile.close()
def _appendrow(self, result, report): import pytest_mozwebqa (testclass, testmethod) = pytest_mozwebqa.split_class_and_test_names(report.nodeid) time = getattr(report, 'duration', 0.0) links = {} if hasattr(report, 'debug') and any(report.debug.values()): (relative_path, full_path) = self._debug_paths(testclass, testmethod) if report.debug['screenshots']: filename = 'screenshot.png' f = open(os.path.join(full_path, filename), 'wb') f.write(base64.decodestring(report.debug['screenshots'][-1])) links.update({'Screenshot': os.path.join(relative_path, filename)}) if report.debug['html']: filename = 'html.txt' f = open(os.path.join(full_path, filename), 'wb') f.write(report.debug['html'][-1]) links.update({'HTML': os.path.join(relative_path, filename)}) # Log may contain passwords, etc so we only capture it for tests marked as public if report.debug['logs'] and 'public' in report.keywords: filename = 'log.txt' f = open(os.path.join(full_path, filename), 'wb') f.write(report.debug['logs'][-1]) links.update({'Log': os.path.join(relative_path, filename)}) if report.debug['network_traffic']: filename = 'networktraffic.json' f = open(os.path.join(full_path, filename), 'wb') f.write(report.debug['network_traffic'][-1]) links.update({'Network Traffic': os.path.join(relative_path, filename)}) if report.debug['urls']: links.update({'Failing URL': report.debug['urls'][-1]}) if self.config.option.sauce_labs_credentials_file and hasattr(report, 'session_id'): self.sauce_labs_job = sauce_labs.Job(report.session_id) if hasattr(self, 'sauce_labs_job'): links['Sauce Labs Job'] = self.sauce_labs_job.url links_html = [] for name, path in links.iteritems(): links_html.append(html.a(name, href=path)) links_html.append(' ') additional_html = [] if not 'Passed' in result: if hasattr(self, 'sauce_labs_job'): additional_html.append(self.sauce_labs_job.video_html) if 'Screenshot' in links: additional_html.append( html.div( html.a(html.img(src=links['Screenshot']), href=links['Screenshot']), class_='screenshot')) if report.longrepr: log = html.div(class_='log') for line in str(report.longrepr).splitlines(): separator = line.startswith('_ ' * 10) if separator: log.append(line[:80]) else: exception = line.startswith("E ") if exception: 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) self.test_logs.append(html.tr([ html.td(result, class_='col-result'), html.td(testclass, class_='col-class'), html.td(testmethod, class_='col-name'), html.td(round(time), class_='col-duration'), html.td(links_html, class_='col-links'), html.td(additional_html, class_='debug')], class_=result.lower() + ' results-table-row'))
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 pytest_sessionfinish(self, session, exitstatus, __multicall__): self._make_report_dir() logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8') suite_stop_time = time.time() suite_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed + self.xpassed + self.xfailed server = self.config.option.sauce_labs_credentials_file and \ 'Sauce Labs' or 'http://%s:%s' % (self.config.option.host, self.config.option.port) browser = self.config.option.browser_name and \ self.config.option.browser_version and \ self.config.option.platform and \ '%s %s on %s' % (str(self.config.option.browser_name).title(), self.config.option.browser_version, str(self.config.option.platform).title()) or \ self.config.option.environment or \ self.config.option.browser generated = datetime.datetime.now() configuration = { 'Base URL': self.config.option.base_url, 'Build': self.config.option.build, 'Selenium API': self.config.option.api, 'Driver': self.config.option.driver, 'Firefox Path': self.config.option.firefox_path, 'Google Chrome Path': self.config.option.chrome_path, 'Selenium Server': server, 'Browser': browser, 'Timeout': self.config.option.webqatimeout, 'Capture Network Traffic': self.config.option.capture_network, 'Credentials': self.config.option.credentials_file, 'Sauce Labs Credentials': self.config.option.sauce_labs_credentials_file} import pytest_mozwebqa doc = html.html( html.head( html.title('Test Report'), html.style( 'body {font-family: Helvetica, Arial, sans-serif; font-size: 12px}\n', 'body * {box-sizing: -moz-border-box; box-sizing: -webkit-border-box; box-sizing: border-box}\n', 'a {color: #999}\n', 'h2 {font-size: 16px}\n', 'table {border: 1px solid #e6e6e6; color: #999; font-size: 12px; border-collapse: collapse}\n', '#configuration tr:nth-child(odd) {background-color: #f6f6f6}\n', '#results {width:100%}\n', 'th, td {padding: 5px; border: 1px solid #E6E6E6; text-align: left}\n', 'th {font-weight: bold}\n', 'tr.passed, tr.skipped, tr.xfailed, tr.error, tr.failed, tr.xpassed {color: inherit}\n' 'tr.passed + tr.additional {display: none}\n', '.passed {color: green}\n', '.skipped, .xfailed {color: orange}\n', '.error, .failed, .xpassed {color: red}\n', '.log:only-child {height: inherit}\n', raw('.log {background-color: #e6e6e6; border: 1px solid #e6e6e6; color: black; display: block; font-family: "Courier New", Courier, monospace; height: 230px; overflow-y: scroll; padding: 5px; white-space:pre-wrap}\n'), '.screenshot, .video {border: 1px solid #e6e6e6; float:right; height:240px; margin-left:5px; overflow:hidden; width:320px}\n', '.screenshot img {width: 320px}')), html.body( html.p('Report generated on %s at %s by pytest-mozwebqa %s' % ( generated.strftime('%d-%b-%Y'), generated.strftime('%H:%M:%S'), pytest_mozwebqa.__version__)), html.h2('Configuration'), html.table( [html.tr(html.td(k), html.td(v)) for k, v in sorted(configuration.items()) if v], id='configuration'), html.h2('Summary'), html.p( '%i tests ran in %i seconds.' % (numtests, suite_time_delta), html.br(), html.span('%i passed' % self.passed, class_='passed'), ', ', html.span('%i skipped' % self.skipped, class_='skipped'), ', ', html.span('%i failed' % self.failed, class_='failed'), ', ', html.span('%i errors' % self.errors, class_='error'), '.', html.br(), html.span('%i expected failures' % self.xfailed, class_='skipped'), ', ', html.span('%i unexpected passes' % self.xpassed, class_='failed'), '.'), html.h2('Results'), html.table( html.tr(html.th('Result'), html.th('Class'), html.th('Name'), html.th('Duration'), html.th('Links')), *self.test_logs, id='results'))) logfile.write(doc.unicode(indent=2)) logfile.close()
def pytest_sessionfinish(self, session, 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()