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

org.specs2.reporter.HtmlBodyPrinter.scala Maven / Gradle / Ivy

There is a newer version: 4.20.8
Show newest version
package org.specs2
package reporter

import io.{DirectoryPath, FilePath}
import main.Arguments
import specification.core._
import specification.process._
import execute._
import text.NotNullStrings._

import scala.xml.NodeSeq
import matcher._
import form._
import control._
import ExecuteActions._
import org.specs2.concurrent.ExecutionEnv
import org.specs2.control.producer.{Transducer, Transducers}
import org.specs2.text.AnsiColors
import origami._
import org.specs2.time.SimpleTimer

/**
 * Create the body of an html file reporting a specification execution
 */
trait HtmlBodyPrinter {

  /**
   * Make the body of the Html file based on all the specification fragments
   */
  def makeBody(spec: SpecStructure, stats: Stats, timer: SimpleTimer, options: HtmlOptions, arguments: Arguments, pandoc: Boolean)(ee: ExecutionEnv): Operation[String] = {
    val title = spec.name
    type HtmlState = (String, Level)

    // Br (new line) fragment's description is meant for console output but
    // in html output examples are embedded in 
  • tags and // there is no need to render additional blank line between them val deleteLineBetweenExamples: Transducer[ActionStack, Fragment, Fragment] = producer => producer.pipe(Transducers.zipWithPreviousAndNext).filter { case (Some(f1), f2, Some(f3)) if Fragment.isExample(f1) && Fragment.isBr(f2) && Fragment.isExample(f3) => false case _ => true }.map { case (_, f, _) => f } val htmlFold = fold.fromFoldLeft[Action, Fragment, HtmlState](("", Level())) { case ((htmlString, level), fragment) => fragment.executionResult.map { result => (htmlString + printFragment(fragment, result, arguments, level, options.outDir, pandoc), Levels.fold(fragment, level)) } } Operations.delayed(spec.fragments.contents.pipe(deleteLineBetweenExamples).fold(htmlFold).runOption(ee)).map { case Some((html, _)) => html + s"""${printStatistics(title, stats, timer, options)}""" case None => s"" } } /** * Print a Fragment as a piece of Html * * If pandoc is true we make sure that text is not parsed in order to be correctly rendered as Markdown */ def printFragment(fragment: Fragment, result: Result, arguments: Arguments, level: Level, baseDir: DirectoryPath, pandoc: Boolean): NodeSeq = { fragment match { case t if Fragment.isText(t) => // remove Ansi colors in case some of them are present in the text val text = AnsiColors.removeColors(t.description.show) if (text.trim.nonEmpty) { if (pandoc) scala.xml.Unparsed(text) else { // remove additional newlines and replace with just one when there is no markdown formatting val brStart = if (text.filterNot(_ == ' ').startsWith("\n"))
    else NodeSeq.Empty val brEnd = if (text.filterNot(_ == ' ').endsWith("\n"))
    else NodeSeq.Empty {brStart}{scala.xml.Unparsed(text.trim)}{brEnd} } } else NodeSeq.Empty case Fragment(form @ FormDescription(_),_,_) => form.xml(arguments) case e if Fragment.isExample(e) => val example = result match { case r: Success =>
  • {show(e)}
  • case f1 @ Failure(m, e1, st, details) => failureElement("example", f1, show(e), m, arguments.failtrace, details, arguments) case er @ Error(m, e1) => errorElement("example", er, show(e), m, arguments) case r: Skipped =>
  • {show(e)}
    {r.message}
  • case r: Pending =>
  • {show(e)}
    {r.message}
  • // if it is a data table as an auto-example case DecoratedResult(table: DataTable, res) if Description.isCode(fragment.description) =>
    {Form(table).toXml(arguments)}
    // if it is a failed data table case DecoratedResult(table: DataTable, res) if !res.isSuccess =>
  • {show(e)} {Form(table).toXml(arguments)}
  • case DecoratedResult(table: DataTable, res) =>
  • {show(e)}
  • case r =>
  • {show(e)}
    {r.message}
  • } if (level.incrementNext)
      {example}
    else example case f if Fragment.isStepOrAction(f) => result match { case f1 @ Failure(m, e1, st, details) => failureElement("step", f1, Failed step!, m, arguments.failtrace, details, arguments) case er @ Error(m, e1) => errorElement("step", er, Error in a step!, m, arguments) case other => NodeSeq.Empty } case Fragment(ref: SpecificationRef,x,_) if !ref.hidden => if (ref.muted) { {ref.linkText} } else { val status = result.statusName(arguments)+" ok" val image = if (fragment.isExecutable) else NodeSeq.Empty {image} {ref.linkText} } case Fragment(Br,_,_) =>
    case other => NodeSeq.Empty } } def toggleElement(a: Any) = "toggleImage(this); showHide('"+id(a)+"')" def id(a: Any) = System.identityHashCode(a).toString def show(f: Fragment) = f.description match { case Code(text) => {text} case other => val d = f.description.show {if (Seq("*", "-").exists(d.trim.startsWith)) d.trim.drop(1) else d} } def showStacktrace(id: String, st: List[StackTraceElement], klass: String, arguments: Arguments) = def failureElement(element: String, f: Result with ResultStackTrace, description: Any, m: String, showTrace: Boolean, details: Details, arguments: Arguments) = { val message = {m.notNull+" ("+f.location(arguments.traceFilter)+")"} val detailedFailure = details match { case FailureDetails(expected, actual) if arguments.diffs.show(expected, actual) =>
    "\nExpected:\n"{expected}"\nActual:\n"{actual}
    case FailureSeqDetails(expected, actual) if arguments.diffs.showSeq(expected, actual, ordered = true) => val (added, missing) = arguments.diffs.showSeqDiffs(actual, expected, ordered = true) val addedValues = makeDifferencesMessage("Added", added) val missingValues = makeDifferencesMessage("Missing", missing) val details = List(addedValues, missingValues).mkString("\n")
    {details}
    case FailureSetDetails(expected, actual) if arguments.diffs.showSeq(expected.toSeq, actual.toSeq, ordered = false) => val (added, missing) = arguments.diffs.showSeqDiffs(actual.toSeq, expected.toSeq, ordered = true) val addedValues = makeDifferencesMessage("Added", added) val missingValues = makeDifferencesMessage("Missing", missing) val details = List(addedValues, missingValues).mkString("\n")
    {details}
    case FailureMapDetails(expected, actual) if arguments.diffs.showMap(actual, expected)=> val (added, missing, different) = arguments.diffs.showMapDiffs(actual, expected) val addedValues = makeDifferencesMessage("Added", added) val missingValues = makeDifferencesMessage("Missing", missing) val differentValues = makeDifferencesMessage("Different", different) val details = List(addedValues, missingValues, differentValues).mkString("\n")
    {details}
    case _ => NodeSeq.Empty } val fullMessage = if (showTrace)
  • {message}{detailedFailure}
  • else
  • {message}{detailedFailure}
  • val trace = if (showTrace) showStacktrace(id(f), f.stackTrace, "failure", arguments) else NodeSeq.Empty
  • {description}
    {fullMessage} {trace}
  • } def errorElement(element: String, er: Result with ResultStackTrace, description: Any, m: String, arguments: Arguments) = {
  • {description}
  • {m.notNull+" ("+er.location(arguments.traceFilter)+")"}
  • {showStacktrace(id(er), er.stackTrace, "error", arguments)} } /** * differences message */ def makeDifferencesMessage(description: String, values: Seq[Any]): String = if (values.nonEmpty) s"\n$description (${values.size}) ${values.map(notNullPair).mkString("\n", "\n", "\n")}" else "" def printStatistics(title: String, stats: Stats, timer: SimpleTimer, options: HtmlOptions) = if (options.noStats) "" else { val statsClass = if (stats.hasErrors) "error" else if (stats.hasIssues) "failure" else "success"
    {s"Total for specification ${title.trim}"}
    Finished in{stats.copy(timer = timer).time}
    Results {stats.displayResults(Arguments("nocolor"))}
    } } object HtmlBodyPrinter extends HtmlBodyPrinter




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy