All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs.runner.Html.scala Maven / Gradle / Ivy

/**
 * Copyright (c) 2007-2009 Eric Torreborre 
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of
 * the Software. Neither the name of specs nor the names of its contributors may be used to endorse or promote
 * products derived from this software without specific prior written permission.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package org.specs.runner
import scala.xml._
import org.specs.specification._
import org.specs.io._
import org.specs.util._
import org.specs.util.ExtendedThrowable._
import org.specs.xml.NodeFunctions._
import org.specs.execute._
import org.specs._

/**
 * The Html trait outputs the results of a specification execution as an html
 * file in a given output directory.
 *
 * The default file name for the report is "specs-report.html" and that report
 * contains all examples description with their execution status: error, failure, success, skipped.
 */
trait Html extends File {
  /** definition of the file name of a specification. */
  override def fileName(spec: BaseSpecification): String = HtmlNamingFunction.default(spec)

  /** definition of the output directory of the report. */
  override def outputDir = normalize(htmlDir)

  /** default directory name. */
  def htmlDir = "target"

  /** report the specification held by this runner. */
  override def report(specifications: Seq[Specification]) = {
    // reuse the inherited method using the specOutput method
    super.report(specifications)
    // provide the additional resources for the html files
    copySpecResourcesDir("images", outputDir)
    copySpecResourcesDir("css", outputDir)

    debug("Html - reporting to " + outputDir + ": " + specs.map(_.description).mkString(", "))
    this
  }
  
  /** define the html content for this specification execution. */
  def specOutput(spec: Specification): String = {
    // it is necessary to replace the br blocks because:
    // 

: gets interpreted as a double line break in html //
: gets interpreted as a single line break in html asHtml(spec).toString.replace("

", "
") } /** * Create the html content for this specification execution. * * The html page is composed of a left column with a table summarizing the status for all systems * and a right (larger) column with a table containing all examples. */ def asHtml(spec: Specification): Elem = {head(spec)} {breadcrumbs(spec)}
{anchorName("top")} {summaryTable(spec)}
{specificationTable(spec)}
/** * head declaration for the specification. * * The title of the document is the specification name. */ def head(specification: Specification) = {specification.name} /** create breadcrumbs links for a specification, starting with the oldest parent */ def breadcrumbs(spec: Specification) = { if (!spec.parentSpecifications.isEmpty) { } else NodeSeq.Empty } /** @return the path to a specification report file */ def specificationLink(spec: BaseSpecification) = { val filePath = spec match { case s: BaseSpecification with Html => s.fileName(spec) case _ => HtmlNamingFunction.default(spec) } {spec.name} } /** * returns a table with the name of all systems, with their status, * possibly shortened if the system's description is too long. */ def summaryTable(specification: Specification) = { /** returns the title of the specification spanning 2 columns for the summary table. */ def specNavHeader = { {specification.name} }
{ if (nonTrivialSpec(specification)) { { specNavHeader } { summarySpec(specification) }
} else NodeSeq.Empty }
} def summarySpec(spec: Specification): NodeSeq = reduce[Sus](spec.systems, summarySus(_, spec)) ++ reduce[Specification](spec.subSpecifications, summarySpec(_)) /** creates a summary row for a sus. */ def summarySus(sus: Sus, spec: Specification): NodeSeq = {statusIcon(sus)} {anchorRef(susName(sus, spec))} /** * creates an anchor reference for a given name, * possibly shortening it for the left column display, but leaving the full name * as a tooltip. */ def anchorRef(name: String) = { {shorten(name)} } /** creates an anchor name, sanitizing the name. */ def anchorName(name: String) = /** sanitize a string so that it can be used as a href */ def sanitize(s: String) = java.net.URLEncoder.encode(s, "UTF-8") /** shorten a string to 30 characters maximum. */ def shorten(s: String) = if (s.size <= 27) s else (s.take(27) + "...") /** create tables for all the subspecifications. */ def subspecsTables(subSpecs: List[Specification]): NodeSeq = reduce[Specification](subSpecs, specificationTable(_)) /** create a table for one specification. */ def specificationTable(spec: Specification) = { spec.linkedSpecifications.foreach(_.reportSpecs)

{spec.description}

++ subspecsTables(spec.unlinkedSpecifications.toList) ++ susTables(spec) } /** create tables for systems. */ def susTables(spec: Specification): NodeSeq = reduce[Sus](spec.systems, susTable(_, spec)) /** create a table for a system. */ def susTable(sus: Sus, spec: Specification): NodeSeq = { anchorName(susName(sus, spec)) ++ susHeader(sus) ++ sus.literateDesc ++ examplesTable(sus) } /** return the sus header if it is not empty, otherwise return the spec name. */ def susName(sus: Sus, spec: Specification) = if (sus.header.trim.isEmpty) spec.name else sus.header /** return the sus header if not empty or an Empty Node. */ def susHeader(sus: Sus) = { if (!sus.header.trim.isEmpty)

{sus.header}{upArrow}

.toSeq else NodeSeq.Empty } def examplesTable(sus: Sus): NodeSeq = { sus.literateDescription match { case None => { {exampleRows(sus.examples, sus.isFullSuccess)}
} case Some(_) if !sus.examples.isEmpty => {

Examples summary

} case _ => NodeSeq.Empty } } /** create an up arrow with an anchor ref to the top. */ def upArrow =
/** create rows for each example, alternating style. */ def exampleRows(examples: Iterable[Example], fullSuccess: Boolean): NodeSeq = examples.toList.foldLeft((NodeSeq.Empty.toSeq, true)) { (result, ex) => val (node, alternation) = result (node ++ example(ex, alternation, fullSuccess), !alternation) }._1 /** * create a row for an example and its subexamples. * * If the example has subexamples, a small header is created. */ def example(example: Example, alternation: Boolean, fullSuccess: Boolean) = { example.examples.toList match { case Nil => exampleRow(example, alternation, fullSuccess) case subexamples =>

{example.exampleDescription.toXhtml.text}

++ exampleRows(subexamples, fullSuccess) } } /** * create a row for an example with its status, description and message. */ def exampleRow(example: Example, alternation: Boolean, fullSuccess: Boolean) = { {statusIcon(example)} {example.exampleDescription.toXhtml}{message(example, fullSuccess)} } /** * status icon for anything having results (errors, failures, skipped). */ def statusIcon(result: HasResults) = { } def image(result: HasResults) = { "images/icon_" + result.statusClass + "_sml.gif" } /** Message for an example. */ def message(example: Example, fullSuccess: Boolean) = { def msg = { if (!example.failures.isEmpty) reduce[FailureException](example.failures, failure(_)) else if (!example.errors.isEmpty) reduce[Throwable](example.errors, e => exceptionText(e)) else if (!example.skipped.isEmpty) reduce[SkippedException](example.skipped, s => exceptionText(s)) else "" } if (fullSuccess) NodeSeq.Empty else {msg} } /** * the failure message for an example is displayed differently depending on its nature. * Failures for DataTables will be reported in a nested table. */ def failure(f: FailureException): NodeSeq = { f match { case e: DataTableFailureException => e.table.toXhtml case regular => exceptionText(regular) } } def stackTrace(e: Throwable) = if (!e.isInstanceOf[FailureException]) e.stackToString("\r", "\r", "") else "" def exceptionText(e: Throwable) = {if (e.getMessage != null) new Text(e.getMessage) else new Text("null")} def initFunction(specification: Specification) = { """function init() { """ + "prettyPrint()" + (if (nonTrivialSpec(specification)) "" else ";noNavBar()") + "}" } def nonTrivialSpec(specification: Specification) = { (specification.systems ++ specification.unlinkedSpecifications).size > 1 } def javaScript(specification: Specification) = } object HtmlNamingFunction { val default = { (s: BaseSpecification) => NamingFunction.default(s) + ".html" } val short = { (s: BaseSpecification) => NamingFunction.short(s) + ".html" } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy