clairvoyance.scalatest.export.ScalaTestHtmlFormat.scala Maven / Gradle / Ivy
package clairvoyance.scalatest.export
import clairvoyance.export._
import clairvoyance.rendering.{CustomRendering, Rendering}
import clairvoyance.rendering.Markdown.markdownToXhtml
import clairvoyance.rendering.Reflection.tryToCreateObject
import clairvoyance.scalatest.{SkipInteractions, SkipSpecification, Tags}
import clairvoyance.scalatest.ClairvoyantContext.tagNames
import clairvoyance.state.{TestState, TestStates}
import java.util.UUID
import org.scalatest.events._
import org.scalatest.{Tag, exceptions}
import scala.xml.NodeSeq
case class ScalaTestHtmlFormat (override val xml: NodeSeq = NodeSeq.Empty) extends HtmlFormat(xml) {
type Self = ScalaTestHtmlFormat
protected def print(xml2: NodeSeq): Self = ScalaTestHtmlFormat(xml ++ xml2)
def printSidebar(results: Seq[SuiteResult]): Self = print(sidebar(results))
private def sidebar(results: Seq[SuiteResult]): NodeSeq = {
val summarisedSuites = results.map { suite =>
{suite.suiteName}
{
suite.eventList.flatMap {
case event: ScopeOpened => stringToPrintWhenNoError(event.formatter, event.nameInfo.suiteName) match {
case Some(text) => Some(- {formatShortExampleName(text)}
)
case None => NodeSeq.Empty
}
case event: TestStarting => Some(- - {formatShortExampleName(event.testText)}
)
case _ => None
}
}
}
}
private def tableOfContentsFor(suiteResult: SuiteResult): NodeSeq = {
{
suiteResult.eventList.map {
case event: TestSucceeded => renderFragmentForTableOfContents(event)
case TestFailedOrCancelled(event) => renderFragmentForTableOfContents(event)
case TestPendingOrIgnored (event) => renderFragmentForTableOfContents(event)
case _ => NodeSeq.Empty
}
}
}
private def renderFragmentForTableOfContents(event: TestSucceeded): NodeSeq =
renderFragmentForTableOfContents(event.testName, event.testText, event.recordedEvents, "test-passed", "test_passed")
private def renderFragmentForTableOfContents(event: TestFailedOrCancelled): NodeSeq =
renderFragmentForTableOfContents(event.testName, event.testText, event.recordedEvents, "test-failed", event.cssClass)
private def renderFragmentForTableOfContents(event: TestPendingOrIgnored): NodeSeq =
renderFragmentForTableOfContents(event.testName, event.testText, event.recordedEvents, "test-not-run", event.cssClass)
private def renderFragmentForTableOfContents(testName: String, testText: String,
recordedEvents: IndexedSeq[RecordableEvent],
listCssClass: String, cssClass: String): NodeSeq =
{formatShortExampleName(testName)}
def printBody(specificationTitle: String, suiteResult: SuiteResult): Self = print(
{wordify(specificationTitle)}
{tableOfContentsFor(suiteResult)}
{
suiteResult.eventList.map {
case ScopeOpenedOrPending(event) => renderFragmentForBody(event)
case event: TestSucceeded => renderFragmentForBody(event)
case TestFailedOrCancelled(event) => renderFragmentForBody(event)
case TestPendingOrIgnored(event) => renderFragmentForBody(event)
case event: MarkupProvided => renderFragmentForBody(event, "markup")
// AlertProvided, InfoProvided, and NoteProvided must not show up in the HTML report
case _ => NodeSeq.Empty
}
}
)
private def renderFragmentForBody(event: ScopeOpenedOrPending): NodeSeq =
stringToPrintWhenNoError(event.formatter, event.nameInfo.suiteName) match {
case Some(string) => markdownToXhtml("# " + string)
case None => NodeSeq.Empty
}
private def renderFragmentForBody(event: TestSucceeded): NodeSeq = {
val (suiteClassName, testName, testText, duration) = (event.suiteClassName.get, event.testName, event.testText, event.duration)
val annotations = annotationsFor(event.suiteName, event.testName)
val testState = TestStates.dequeue(testName)
val rendering = renderingFor(suiteClassName)
{markdownToXhtml(s"## $testText")}
{ if (!annotations.contains(SkipSpecification))
Specification
{SpecificationFormatter.format(getCodeFrom(suiteClassName, event), Seq.empty, suiteClassName, codeFormatFor(suiteClassName))}
}
Execution
{duration.fold("")(milliseconds => s"Passed in $milliseconds ms")}
{interestingGivensTable(testState, rendering)}
{capturedInputsAndOutputs(testState, rendering, annotations)}
}
private def getCodeFrom(location: String, event: TestSucceeded): List[(Int, String)] = {
event.location match {
case Some(LineInFile(ln, _)) => FromSource.getCodeFrom(location, ln)
case a@_ => FromSource.getCodeFrom(location, event.testText)
}
}
private def renderFragmentForBody(event: TestFailedOrCancelled): NodeSeq = {
val annotations = annotationsFor(event.suiteName, event.testName)
val testState = TestStates.dequeue(event.testName)
val rendering = renderingFor(event.suiteClassName)
val linkId = UUID.randomUUID.toString
val contentId = UUID.randomUUID.toString
val (grayStackTraceElements, blackStackTraceElements) =
event.throwable match {
case Some(throwable) =>
val stackTraceElements = throwable.getStackTrace.toList
throwable match {
case sde: exceptions.StackDepthException => (stackTraceElements.take(sde.failedCodeStackDepth), stackTraceElements.drop(sde.failedCodeStackDepth))
case _ => (List(), stackTraceElements)
}
case None => (List(), List())
}
{markdownToXhtml("## " + event.testText)}
{ if (!annotations.contains(SkipSpecification))
Specification
{
val sourceLines = FromSource.getCodeFrom(event.suiteClassName, event.testText)
SpecificationFormatter.format(sourceLines, event.throwable.get.getStackTrace.toList, event.suiteClassName, codeFormatFor(event.suiteClassName))
}
}
Execution
{ event.duration.fold(NodeSeq.Empty)(milliseconds => {event.name} after {milliseconds} ms
)}
> { event.message }
[ show stacktrace ]
{interestingGivensTable(testState, rendering)}
{capturedInputsAndOutputs(testState, rendering, annotations)}
}
private def capturedInputsAndOutputs(testState: Option[TestState], rendering: Rendering, annotations: Set[Tag]): NodeSeq = {
{loggedInputsAndOutputs(testState.map(x => x.copy(x.interestingGivens, x.capturedInputsAndOutputs.filterNot(_.key.matches(".*(Graph|Diagram).*")))), rendering)}
{loggedInputsAndOutputs(testState.map(x => x.copy(x.interestingGivens, x.capturedInputsAndOutputs.filter(_.key.matches(".*(Graph|Diagram).*")))), rendering)}
}
private def renderFragmentForBody(event: TestPendingOrIgnored): NodeSeq = {
{markdownToXhtml("## " + event.testText)}
Specification
{SpecificationFormatter.format(FromSource.getCodeFrom(event.suiteClassName, event.testText), Seq.empty, event.suiteClassName, codeFormatFor(event.suiteClassName))}
Execution
{event.name}
}
private def renderFragmentForBody(event: MarkupProvided, cssClass: String): NodeSeq = markdownToXhtml(event.text)
private def stringToPrintWhenNoError(formatter: Option[Formatter], suiteName: String): Option[String] = {
formatter match {
case Some(IndentedText(_, rawText, _)) => Some(rawText)
case Some(MotionToSuppress) => None
case _ => Some(suiteName)
}
}
private def renderingFor(className: String): Rendering = new Rendering(tryToCreateObject[CustomRendering](className))
private def codeFormatFor(className: String): CodeFormat = tryToCreateObject[CodeFormat](className).getOrElse(DefaultCodeFormat)
private def annotationsFor(suiteName: String, testName: String): Set[Tag] = {
val tags = tagNames((suiteName, testName))
Tags.declared.filter(t => tags.contains(t.name))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy