org.specs2.reporter.HtmlResultOutput.scala Maven / Gradle / Ivy
package org.specs2
package reporter
import scala.xml._
import control._
import main.{ Arguments, Diffs }
import text.Markdown._
import text._
import io.Paths._
import NotNullStrings._
import text.Trim._
import execute._
import xml.Nodex._
import specification._
import html.Htmlx._
/**
* This class stores the html to print to a file (as a NodeSeq object)
*
* An instance of that class is immutable so each print method returns another instance
* containing more xml to print.
*
*/
private[specs2]
case class HtmlResultOutput(xml: NodeSeq = NodeSeq.Empty, filePath: String = "", textPrinter: String => NodeSeq = toXhtml(_)) extends HtmlReportOutput { outer =>
/**
* start of the output
*/
private[specs2] lazy val blank = new HtmlResultOutput(NodeSeq.Empty, outer.filePath)
/** set the file path of the file being written */
def filePathIs(path: String) = copy(filePath = path)
/** base directory for this file path */
def baseDir = filePath.baseDir
/** print the NodeSeq inside the html tags */
def printHtml(n: =>NodeSeq) = print({n})
/** print the NodeSeq inside the body tags, with anchors for header tags */
def printBody(n: =>NodeSeq) = print(({n}).addHeadersAnchors)
/** print the head of the document */
def printHead = print(xml ++ head)
/** print a br tag */
def printBr = printOkStatus(
)
/** print a paragraph for some text */
def printPar(text: String = "") = printOkStatus({wiki(text)}
)
/** print some text */
def printText(text: String = "", level: Int = 0) = printOkStatus(div(wiki(text), level))
/** print some text in a paragraph */
def printTextPar(text: String = "", level: Int = 0) = printOkStatus(p(wiki(text), level))
/**
* print the xhtml for a specification start:
*
* - the html title is the specification title
* - if there are no issues then print a h2 header with the title of the specification
* - if there are issues add a toggle link so that only issues can be displayed
*/
def printSpecStart(name: SpecName, stats: Stats) = {
print({name.title} ).
print {
val header =
if (stats.hasIssues)
{name.title}
{showOnlyShowAllLinks(elementClass = "ok", "(issues only)", "(all)")}
else
{name.title}
header.updateHeadAttribute("specId", name.id)
}
}
/**
* print a link
*
* - if it is a link to another specification:
* - if it has issues, set a corresponding icon
* - add a subtoc element with the specification id so that the TableOfContents class knows where to insert the sub table content corresponding
* to the linked specification when building the global table of contents
*
* - if this is an arbitrary link, print a normal html link
*/
def printLink(link: HtmlLink, level: Int, stats: Stats = Stats(), hidden: Boolean = false) = {
val linkStatus = if (stats.hasIssues) "ko" else "ok"
val htmlLink = outer.copy(xml = NodeSeq.Empty).printLink(link).xml
link match {
case slink @ SpecHtmlLink(name, before, l, after, tip) => {
print().
printStatus(div( ++ t(" ") ++ htmlLink, level, hidden), linkStatus)
}
case UrlHtmlLink(url, before, l, after, tip) => printStatus(div(htmlLink, level, hidden), linkStatus)
}
}
def printLink(link: HtmlLink) = print(wiki(link.beforeText) ++ {wiki(link.linkText)} ++ wiki(link.afterText))
/** print some text with a status icon (with an ok class) */
def printTextWithIcon(message: MarkupString, iconName: String, level: Int = 0) = printOkStatus(textWithIcon(message, iconName, level))
/** print some xml with a status icon (with an ok class) */
def printOkXmlWithIcon(xml: NodeSeq, iconName: String, level: Int = 0) = printOkStatus(xmlWithIcon(xml, iconName, level))
/** print some xml with a status icon (with an ok class) */
def printKoXmlWithIcon(xml: NodeSeq, iconName: String, level: Int = 0) = printKoStatus(xmlWithIcon(xml, iconName, level))
/** print an issue with a status icon (with a ko class) */
def printIssueWithIcon(message: MarkupString, iconName: String, level: Int = 0) = printKoStatus(textWithIcon(message, iconName, level))
/** print an exception message (with a ko class) */
def printExceptionMessage(e: Result with ResultStackTrace, level: Int) = printKoStatus(div(" "+e.message+" ("+e.location+")", level))
/**
* print a collapsible exception message (with a ko class)
*
* the message in enclosed in a div which has a unique id and associated onclick function to show/hide it
*/
def printCollapsibleExceptionMessage(e: Result with ResultStackTrace, level: Int) =
printKoStatus(div( ++
t(" "+e.message.notNull+" ("+e.location+")"), level))
/**
* print the details of a Failure message in a collapsible div
*/
def printDetailedFailure(details: Details, level: Int, diffs: Diffs) = {
details match {
case FailureDetails(expected, actual) if diffs.show(expected, actual) => {
val (expectedDiff, actualDiff) = diffs.showDiffs(expected, actual)
val (expectedMessage, actualMessage) = ("Expected: " + expectedDiff, "Actual: " + actualDiff)
val (expectedFull, actualFull) = ("Expected (full): " + expected, "Actual (full): " + actual)
printKoStatus(div( ++ t("details"), level) ++
)
}
case _ => this
}
}
/**
* print a stacktrace for a failure or an exception
*
* The stacktrace elements are filtered with the traceFilter parameter
*/
def printStack(e: ResultStackTrace, level: Int, traceFilter: StackTraceFilter) =
enclose((t: NodeSeq) => koStatus( )) {
traceFilter(e.stackTrace).foldLeft(blank) { (res, cur) => res.printText(cur.toString, level) }
}
/**
* print the final statistics for a specification as a table
*/
def printStats(n: SpecName, stats: Stats) = {
val title = "Total for specification" + ((" "+n.name.trim) unless n.name.isEmpty)
val classStatus = if (stats.hasIssues) "failure" else "success"
print(
{title}
Finished in {stats.time}
Results { stats.displayResults(Arguments("nocolor")) }
)
}
/**
* print the html for a Form, by just adding the corresponding xml to the current output
*/
def printForm(form: NodeSeq) = print(form)
protected def printOkStatus(n: NodeSeq) = print(okStatus(n))
protected def printKoStatus(n: NodeSeq) = print(koStatus(n))
protected def printStatus(n: NodeSeq, st: String) = print(status(n, st))
protected def textWithIcon(message: MarkupString, iconName: String, level: Int = 0) = div( ++ t(" ") ++ wiki(message.toHtml), level)
protected def xmlWithIcon(xml: NodeSeq, iconName: String, level: Int = 0) = div({xml}
, level)
protected def icon(t: String) = baseDir+"images/icon_"+t+"_sml.gif"
protected def okStatus(n: NodeSeq) = status(n, "ok")
protected def koStatus(n: NodeSeq) = status(n, "ko")
/** print a NodeSeq with a given status class */
protected def status(n: NodeSeq, st: String) = {n}
/** create a div around some markup text to be displayed at a certain level of identation */
protected def div(string: String, level: Int): NodeSeq = div(t(string), level)
/** create a div around a NodeSeq to be displayed at a certain level of identation */
protected def div(n: NodeSeq, level: Int, hidden: Boolean = false): NodeSeq = {n}
/** create a paragraph around a NodeSeq to be displayed at a certain level of identation */
protected def p(n: NodeSeq, level: Int) = {n}
/** create a Text node */
protected def t(text: String): NodeSeq = scala.xml.Text(text)
protected def toggleElement(a: Any) = "toggleImage(this); showHide('"+id(a)+"')"
protected def id(a: Any) = System.identityHashCode(a).toString
/** render some markup text as xhtml */
protected def wiki(text: String) = textPrinter(text)
/**
* Head of the html document. It contains:
*
* - links to the prettify css and javascript functions to render code
* - jquery scripts to render the table of contents as a tree
* - tabber css and scripts to display tabs
* - show and hide functions
*/
def head =
/**
* define custom javascript functions to manipulate elements on the page, mostly to show and hide elements
*/
def javascript =
/**
* Usage: out.enclose((t: NodeSeq) => {t})(inside))
*
* to create inside)
*
* @return some xml (rest) enclosed in another block
*/
private def enclose(f: NodeSeq => NodeSeq)(rest: =>HtmlResultOutput): HtmlResultOutput = print(f(rest.xml))
def print(xml2: NodeSeq): HtmlResultOutput = copy(xml = xml ++ xml2)
def print(xml2: Elem): HtmlResultOutput = copy(xml = xml ++ xml2)
/**
* @param elementClass class of elements to show/hide
* @param showOnlyLabel label to display for the elements to show only
* @param showAllLabel label to display for showing all the elements
*
* @return 2 links allowing to show/hide some elements on a html page
*/
protected def showOnlyShowAllLinks(elementClass: String, showOnlyLabel: String, showAllLabel: String): NodeSeq = {
val (showOnlyId, showAllId) = ("onlyIssuesLink", "allElementsLink")
val showOnlyOnclick = "hideByClass('"+elementClass+"');hideById('"+showOnlyId+"');showById('"+showAllId+"')"
val showAllOnclick = "showByClass('"+elementClass+"');hideById('"+showAllId+"');showById('"+showOnlyId+"')"
{showOnlyLabel} ++
{showAllLabel}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy