rhyskeepence / clairvoyance

@@ -2,12 +2,11 @@
Loading
2 2
3 3
import clairvoyance.export.ClairvoyanceHtml
4 4
5 -
trait ClairvoyanceHtmlPrinter {
5 +
object ClairvoyanceHtmlPrinter {
6 6
7 7
  def print(suiteResult: SuiteResult): ClairvoyanceHtml = {
8 8
    val suiteFileName = asFileName(suiteResult)
9 -
    ClairvoyanceHtml(s"$suiteFileName.html", printHtml(suiteFileName, suiteResult).xml)
10 -
//    printTeamCityLog(executed)
9 +
    ClairvoyanceHtml(s"$suiteFileName.html", printHtml(suiteFileName, suiteResult))
11 10
  }
12 11
13 12
  private def asFileName(suiteResult: SuiteResult) = suiteResult.suiteClassName match {
@@ -18,15 +17,9 @@
Loading
18 17
  private def printHtml(
19 18
      specificationTitle: String,
20 19
      suiteResult: SuiteResult
21 -
  ): ScalaTestHtmlFormat = {
22 -
    clairvoyanceFormat.printHtml(
23 -
      clairvoyanceFormat
24 -
        .printHead(specificationTitle)
25 -
        .printBody(suiteResult.suiteName, suiteResult)
26 -
        .xml
27 -
    )
20 +
  ): String = {
21 +
    clairvoyanceFormat.format(specificationTitle, suiteResult)
28 22
  }
29 23
30 -
  protected def allSuiteResults: Seq[SuiteResult]
31 24
  private def clairvoyanceFormat = new ScalaTestHtmlFormat()
32 25
}

@@ -1,16 +1,13 @@
Loading
1 1
package clairvoyance.rendering
2 2
3 -
import org.pegdown.{Extensions, LinkRenderer, PegDownProcessor, ToHtmlSerializer}
4 3
import org.pegdown.ast.CodeNode
5 -
import scala.io.Source
4 +
import org.pegdown.{Extensions, LinkRenderer, PegDownProcessor, ToHtmlSerializer}
5 +
6 6
import scala.util.matching.Regex
7 -
import scala.xml.NodeSeq
8 -
import scala.xml.parsing.XhtmlParser
9 7
10 8
object Markdown {
11 9
12 -
  /** Inlined from org.specs2.text.Markdown */
13 -
  def markdownToXhtml(markdown: String): NodeSeq = {
10 +
  def markdownToXhtml(markdown: String): String = {
14 11
    val pegDown =
15 12
      new PegDownProcessor(Extensions.ALL & ~Extensions.QUOTES & ~Extensions.SMARTS, 2000L)
16 13
    val htmlSerializer = new ToHtmlSerializer(new LinkRenderer) {
@@ -35,7 +32,7 @@
Loading
35 32
    val htmlWithoutParagraph =
36 33
      if (!markdown.contains("\n") || markdown.trim.isEmpty) html.removeEnclosingXmlTag("p")
37 34
      else html
38 -
    XhtmlParser(Source.fromString(s"<text>$htmlWithoutParagraph</text>"))
35 +
    s"<text>$htmlWithoutParagraph</text>"
39 36
  }
40 37
41 38
  implicit def trimmed(s: String): Trimmed = new Trimmed(s)

@@ -1,36 +1,38 @@
Loading
1 1
package clairvoyance.scalatest.export
2 2
3 +
import java.util.UUID
4 +
3 5
import clairvoyance.export._
4 -
import clairvoyance.rendering.{CustomRendering, Rendering}
5 6
import clairvoyance.rendering.Markdown.markdownToXhtml
6 7
import clairvoyance.rendering.Reflection.tryToCreateObject
7 -
import clairvoyance.scalatest.{SkipInteractions, SkipSpecification, Tags}
8 +
import clairvoyance.rendering.{CustomRendering, Rendering}
8 9
import clairvoyance.scalatest.ClairvoyantContext.tagNames
10 +
import clairvoyance.scalatest.{SkipInteractions, SkipSpecification, Tags}
9 11
import clairvoyance.state.{TestState, TestStates}
10 -
import java.util.UUID
11 12
import org.scalatest.events._
12 13
import org.scalatest.{Tag, exceptions}
13 -
import scala.xml.NodeSeq
14 14
15 -
case class ScalaTestHtmlFormat(override val xml: NodeSeq = NodeSeq.Empty) extends HtmlFormat(xml) {
16 -
  type Self = ScalaTestHtmlFormat
17 -
18 -
  protected def print(xml2: NodeSeq): Self = ScalaTestHtmlFormat(xml ++ xml2)
15 +
class ScalaTestHtmlFormat extends HtmlFormat {
16 +
  def format(specificationTitle: String, suiteResult: SuiteResult): String = {
17 +
    head(specificationTitle) + printBody(suiteResult.suiteName, suiteResult)
18 +
  }
19 19
20 -
  private def tableOfContentsFor(suiteResult: SuiteResult): NodeSeq = {
20 +
  private def tableOfContentsFor(suiteResult: SuiteResult): String = {
21 +
    s"""
21 22
    <ul class="contents">
22 -
    {
23 -
      suiteResult.eventList.map {
23 +
    ${suiteResult.eventList
24 +
      .map {
24 25
        case event: TestSucceeded         => renderFragmentForTableOfContents(event)
25 26
        case TestFailedOrCancelled(event) => renderFragmentForTableOfContents(event)
26 27
        case TestPendingOrIgnored(event)  => renderFragmentForTableOfContents(event)
27 -
        case _                            => NodeSeq.Empty
28 +
        case _                            => ""
28 29
      }
29 -
    }
30 +
      .mkString("\n")}
30 31
    </ul>
32 +
    """
31 33
  }
32 34
33 -
  private def renderFragmentForTableOfContents(event: TestSucceeded): NodeSeq =
35 +
  private def renderFragmentForTableOfContents(event: TestSucceeded): String =
34 36
    renderFragmentForTableOfContents(
35 37
      event.testName,
36 38
      event.testText,
@@ -39,7 +41,7 @@
Loading
39 41
      "test_passed"
40 42
    )
41 43
42 -
  private def renderFragmentForTableOfContents(event: TestFailedOrCancelled): NodeSeq =
44 +
  private def renderFragmentForTableOfContents(event: TestFailedOrCancelled): String =
43 45
    renderFragmentForTableOfContents(
44 46
      event.testName,
45 47
      event.testText,
@@ -48,7 +50,7 @@
Loading
48 50
      event.cssClass
49 51
    )
50 52
51 -
  private def renderFragmentForTableOfContents(event: TestPendingOrIgnored): NodeSeq =
53 +
  private def renderFragmentForTableOfContents(event: TestPendingOrIgnored): String =
52 54
    renderFragmentForTableOfContents(
53 55
      event.testName,
54 56
      event.testText,
@@ -63,40 +65,41 @@
Loading
63 65
      recordedEvents: IndexedSeq[RecordableEvent],
64 66
      listCssClass: String,
65 67
      cssClass: String
66 -
  ): NodeSeq =
67 -
    <li class={listCssClass}>
68 -
      <a href={"#" + linkNameOf(testText)}>
69 -
        {formatShortExampleName(testName)}
68 +
  ): String =
69 +
    s"""<li class="$listCssClass">
70 +
      <a href="#${linkNameOf(testText)}">
71 +
        ${formatShortExampleName(testName)}
70 72
      </a>
71 -
    </li>
73 +
    </li>"""
72 74
73 -
  def printBody(specificationTitle: String, suiteResult: SuiteResult): Self = print(
75 +
  def printBody(specificationTitle: String, suiteResult: SuiteResult): String =
76 +
    s"""
74 77
    <body>
75 78
      <div id="container">
76 -
        <h1> {wordify(specificationTitle)} </h1>
77 -
        {tableOfContentsFor(suiteResult)}
78 -
        {
79 -
      suiteResult.eventList.map {
79 +
        <h1> ${wordify(specificationTitle)} </h1>
80 +
        ${tableOfContentsFor(suiteResult)}
81 +
        ${suiteResult.eventList
82 +
      .map {
80 83
        case ScopeOpenedOrPending(event)  => renderFragmentForBody(event)
81 84
        case event: TestSucceeded         => renderFragmentForBody(event)
82 85
        case TestFailedOrCancelled(event) => renderFragmentForBody(event)
83 86
        case TestPendingOrIgnored(event)  => renderFragmentForBody(event)
84 87
        case event: MarkupProvided        => renderFragmentForBody(event, "markup")
85 88
        // AlertProvided, InfoProvided, and NoteProvided must not show up in the HTML report
86 -
        case _ => NodeSeq.Empty
89 +
        case _ => ""
87 90
      }
88 -
    }
91 +
      .mkString("\n")}
89 92
      </div>
90 93
    </body>
91 -
  )
94 +
    """
92 95
93 -
  private def renderFragmentForBody(event: ScopeOpenedOrPending): NodeSeq =
96 +
  private def renderFragmentForBody(event: ScopeOpenedOrPending): String =
94 97
    stringToPrintWhenNoError(event.formatter, event.nameInfo.suiteName) match {
95 98
      case Some(string) => markdownToXhtml("# " + string)
96 -
      case None         => NodeSeq.Empty
99 +
      case None         => ""
97 100
    }
98 101
99 -
  private def renderFragmentForBody(event: TestSucceeded): NodeSeq = {
102 +
  private def renderFragmentForBody(event: TestSucceeded): String = {
100 103
    val (suiteClassName, testName, testText, duration) =
101 104
      (event.suiteClassName.get, event.testName, event.testText, event.duration)
102 105
    val annotations = annotationsFor(event.suiteName, event.testName)
@@ -104,40 +107,51 @@
Loading
104 107
    val testState = TestStates.dequeue(testName)
105 108
    val rendering = renderingFor(suiteClassName)
106 109
107 -
    <a id={linkNameOf(testText)}></a>
110 +
    s"""
111 +
    <a id="${linkNameOf(testText)}"></a>
108 112
    <div class="testmethod">
109 -
      {markdownToXhtml(s"## $testText")}
110 -
      <div class="scenario" id={testName.hashCode().toString}>
111 -
        {
112 -
      if (!annotations.contains(SkipSpecification))
113 -
        <h2>Specification</h2>
114 -
        <pre class="highlight specification">{
115 -
          SpecificationFormatter.format(
116 -
            getCodeFrom(suiteClassName, event),
117 -
            Seq.empty,
118 -
            suiteClassName,
119 -
            codeFormatFor(suiteClassName)
120 -
          )
121 -
        }</pre>
122 -
    }
113 +
      ${markdownToXhtml(s"## $testText")}
114 +
      <div class="scenario" id="${testName.hashCode().toString}">
115 +
        ${renderSpecification(annotations, suiteClassName, event)}
123 116
        <h2>Execution</h2>
124 -
        <pre class="highlight results test-passed highlighted">{
125 -
      duration.fold("")(milliseconds => s"Passed in $milliseconds ms")
126 -
    }</pre>
127 -
        {interestingGivensTable(testState, rendering)}
128 -
        {capturedInputsAndOutputs(testState, rendering, annotations)}
117 +
        <pre class="highlight results test-passed highlighted">${duration.fold("")(
118 +
      milliseconds => s"Passed in $milliseconds ms"
119 +
    )}</pre>
120 +
        ${interestingGivensTable(testState, rendering)}
121 +
        ${capturedInputsAndOutputs(testState, rendering, annotations)}
129 122
      </div>
130 123
    </div>
124 +
    """
125 +
  }
126 +
127 +
  private def renderSpecification(
128 +
      annotations: Set[Tag],
129 +
      suiteClassName: String,
130 +
      event: TestSucceeded
131 +
  ) = {
132 +
    if (!annotations.contains(SkipSpecification)) {
133 +
      val spec = SpecificationFormatter.format(
134 +
        getCodeFrom(suiteClassName, event),
135 +
        Seq.empty,
136 +
        suiteClassName,
137 +
        codeFormatFor(suiteClassName)
138 +
      )
139 +
140 +
      s"""
141 +
        <h2>Specification</h2>
142 +
        <pre class="highlight specification">${spec}</pre>
143 +
        """
144 +
    } else ""
131 145
  }
132 146
133 147
  private def getCodeFrom(location: String, event: TestSucceeded): List[(Int, String)] = {
134 148
    event.location match {
135 149
      case Some(LineInFile(ln, _, _)) => FromSource.getCodeFrom(location, ln)
136 -
      case a @ _                      => FromSource.getCodeFrom(location, event.testText)
150 +
      case _                          => FromSource.getCodeFrom(location, event.testText)
137 151
    }
138 152
  }
139 153
140 -
  private def renderFragmentForBody(event: TestFailedOrCancelled): NodeSeq = {
154 +
  private def renderFragmentForBody(event: TestFailedOrCancelled): String = {
141 155
    val annotations = annotationsFor(event.suiteName, event.testName)
142 156
    val testState   = TestStates.dequeue(event.testName)
143 157
    val rendering   = renderingFor(event.suiteClassName)
@@ -160,115 +174,111 @@
Loading
160 174
        case None => (List(), List())
161 175
      }
162 176
163 -
    <a id={linkNameOf(event.testText)}></a>
164 -
    <div class="testmethod">
165 -
      {markdownToXhtml("## " + event.testText)}
166 -
      <div class="scenario" id={event.testName.hashCode().toString}>
167 -
        {
168 -
      if (!annotations.contains(SkipSpecification))
177 +
    val specification = {
178 +
      if (!annotations.contains(SkipSpecification)) {
179 +
        val spec = SpecificationFormatter.format(
180 +
          FromSource.getCodeFrom(event.suiteClassName, event.testText),
181 +
          event.throwable.get.getStackTrace.toList,
182 +
          event.suiteClassName,
183 +
          codeFormatFor(event.suiteClassName)
184 +
        )
185 +
186 +
        s"""
169 187
        <h2>Specification</h2>
170 -
        <pre class="highlight specification">{
171 -
          val sourceLines = FromSource.getCodeFrom(event.suiteClassName, event.testText)
172 -
          SpecificationFormatter.format(
173 -
            sourceLines,
174 -
            event.throwable.get.getStackTrace.toList,
175 -
            event.suiteClassName,
176 -
            codeFormatFor(event.suiteClassName)
177 -
          )
178 -
        }</pre>
188 +
        <pre class="highlight specification">${spec}</pre>
189 +
        """
190 +
      } else
191 +
        ""
179 192
    }
193 +
194 +
    s"""
195 +
    <a id="${linkNameOf(event.testText)}"></a>
196 +
    <div class="testmethod">
197 +
      ${markdownToXhtml("## " + event.testText)}
198 +
      <div class="scenario" id="${event.testName.hashCode().toString}">
199 +
        ${specification}
180 200
        <h2>Execution</h2>
181 201
        <div class="highlight results test-failed highlighted" style="margin-bottom: 1em">
182 -
          {
183 -
      event.duration.fold(NodeSeq.Empty)(
184 -
        milliseconds => <span>{event.name} after {milliseconds} ms</span><br/>
185 -
      )
186 -
    }
187 -
          <pre>&gt; {event.message}</pre>
202 +
          ${event.duration
203 +
      .map(milliseconds => s"<span>${event.name} after ${milliseconds} ms</span><br/>")
204 +
      .getOrElse("")}
205 +
          <pre>&gt; ${event.message}</pre>
188 206
          <span class="detailstoggle">
189 -
            <a id={linkId} href="#" onclick={
190 -
      s"toggleDetails('$contentId', '$linkId'); return false"
191 -
    }>[ show stacktrace ]</a>
207 +
            <a id="${linkId}" href="#" onclick="{ toggleDetails('$contentId', '$linkId'); return false; }">[ show stacktrace ]</a>
192 208
          </span>
193 -
          <div id={contentId} style="display: none; margin-top: 1em">
194 -
            {
195 -
      grayStackTraceElements.map(
196 -
        (ste: StackTraceElement) => <div style="color: #CCADAD">{ste.toString}</div>
197 -
      )
198 -
    }
199 -
            {
200 -
      <div style="color: #9C3636; font-weight: bold">{blackStackTraceElements.head.toString}</div>
201 -
    }
202 -
            {
203 -
      blackStackTraceElements.tail.map(
204 -
        (ste: StackTraceElement) => <div style="color: #9C3636">{ste.toString}</div>
205 -
      )
206 -
    }
209 +
          <div id="${contentId}" style="display: none; margin-top: 1em">
210 +
            ${grayStackTraceElements.map(
211 +
      ste => s"""<div style="color: #CCADAD">${ste.toString}</div>)"""
212 +
    )}
213 +
            <div style="color: #9C3636; font-weight: bold">${blackStackTraceElements.head.toString}</div>
214 +
            ${blackStackTraceElements.tail.map(
215 +
      ste => s"""<div style="color: #9C3636">${ste.toString}</div>"""
216 +
    )}
207 217
          </div>
208 218
        </div>
209 -
        {interestingGivensTable(testState, rendering)}
210 -
        {capturedInputsAndOutputs(testState, rendering, annotations)}
219 +
        ${interestingGivensTable(testState, rendering).mkString("\n")}
220 +
        ${capturedInputsAndOutputs(testState, rendering, annotations)}
211 221
      </div>
212 222
    </div>
223 +
    """
213 224
  }
214 225
215 226
  private def capturedInputsAndOutputs(
216 227
      testState: Option[TestState],
217 228
      rendering: Rendering,
218 229
      annotations: Set[Tag]
219 -
  ): NodeSeq = {
220 -
    <div style={if (annotations.contains(SkipInteractions)) "display: none" else "display: inline"}>
221 -
    {
222 -
      loggedInputsAndOutputs(
223 -
        testState.map(
224 -
          x =>
225 -
            x.copy(
226 -
              x.interestingGivens,
227 -
              x.capturedInputsAndOutputs.filterNot(_.key.matches(".*(Graph|Diagram).*"))
228 -
            )
229 -
        ),
230 -
        rendering
231 -
      )
232 -
    }
233 -
    </div>
234 -
    <div stye="display: inline">
235 -
    {
236 -
      loggedInputsAndOutputs(
237 -
        testState.map(
238 -
          x =>
239 -
            x.copy(
240 -
              x.interestingGivens,
241 -
              x.capturedInputsAndOutputs.filter(_.key.matches(".*(Graph|Diagram).*"))
242 -
            )
243 -
        ),
244 -
        rendering
245 -
      )
246 -
    }
247 -
    </div>
230 +
  ): String = {
231 +
    val interactionStyle =
232 +
      if (annotations.contains(SkipInteractions)) "display: none" else "display: inline"
233 +
234 +
    val interactions = loggedInputsAndOutputs(
235 +
      testState.map(
236 +
        x =>
237 +
          x.copy(
238 +
            x.interestingGivens,
239 +
            x.capturedInputsAndOutputs.filterNot(_.key.matches(".*(Graph|Diagram).*"))
240 +
          )
241 +
      ),
242 +
      rendering
243 +
    )
244 +
245 +
    val diagrams = loggedInputsAndOutputs(
246 +
      testState.map(
247 +
        x =>
248 +
          x.copy(
249 +
            x.interestingGivens,
250 +
            x.capturedInputsAndOutputs.filter(_.key.matches(".*(Graph|Diagram).*"))
251 +
          )
252 +
      ),
253 +
      rendering
254 +
    )
255 +
256 +
    s"""
257 +
        <div style="${interactionStyle}">${interactions.mkString("\n")}</div>
258 +
        <div style="display: inline">${diagrams.mkString("\n")}</div>
259 +
      """
248 260
  }
249 261
250 -
  private def renderFragmentForBody(event: TestPendingOrIgnored): NodeSeq = {
262 +
  private def renderFragmentForBody(event: TestPendingOrIgnored): String =
263 +
    s"""
251 264
    <a id={linkNameOf(event.testText)}></a>
252 265
    <div class="testmethod">
253 -
      {markdownToXhtml("## " + event.testText)}<div class="scenario" id={
254 -
      event.testName.hashCode().toString
255 -
    }>
266 +
      ${markdownToXhtml("## " + event.testText)}
267 +
      <div class="scenario" id=${event.testName.hashCode().toString}>
256 268
        <h2>Specification</h2>
257 -
        <pre class="highlight specification">{
258 -
      SpecificationFormatter.format(
259 -
        FromSource.getCodeFrom(event.suiteClassName, event.testText),
260 -
        Seq.empty,
261 -
        event.suiteClassName,
262 -
        codeFormatFor(event.suiteClassName)
263 -
      )
264 -
    }</pre>
269 +
        <pre class="highlight specification">${SpecificationFormatter.format(
270 +
      FromSource.getCodeFrom(event.suiteClassName, event.testText),
271 +
      Seq.empty,
272 +
      event.suiteClassName,
273 +
      codeFormatFor(event.suiteClassName)
274 +
    )}</pre>
265 275
        <h2>Execution</h2>
266 -
        <pre class="highlight specification test-not-run">{event.name}</pre>
276 +
        <pre class="highlight specification test-not-run">${event.name}</pre>
267 277
      </div>
268 278
    </div>
269 -
  }
279 +
  """
270 280
271 -
  private def renderFragmentForBody(event: MarkupProvided, cssClass: String): NodeSeq =
281 +
  private def renderFragmentForBody(event: MarkupProvided, cssClass: String): String =
272 282
    markdownToXhtml(event.text)
273 283
274 284
  private def stringToPrintWhenNoError(

@@ -25,16 +25,17 @@
Loading
25 25
      val target = Path.of(outputDir, src)
26 26
			JFiles.walk(directory).forEach(source => {
27 27
				val targetFile = target.resolve(directory.relativize(source))
28 -
				JFiles.createDirectories(targetFile.getParent)
29 -
				JFiles.copy(source, targetFile, REPLACE_EXISTING)
28 +
        if (JFiles.isDirectory(source))
29 +
          createDirectories(targetFile)
30 +
        else
31 +
          JFiles.copy(source, targetFile, REPLACE_EXISTING)
30 32
			})
31 33
    }
32 34
  }
33 35
34 36
  private def unjar(jarUrl: URL, dirPath: String, regexFilter: String): Unit = {
35 37
    val path = Paths.get(dirPath)
36 -
    if (!JFiles.exists(path))
37 -
      JFiles.createDirectories(path)
38 +
    createDirectories(path)
38 39
39 40
    val zis = new ZipInputStream(new BufferedInputStream(jarUrl.openStream()))
40 41
@@ -61,4 +62,9 @@
Loading
61 62
    extractEntry(zis.getNextEntry)
62 63
    zis.close()
63 64
  }
65 +
66 +
  private def createDirectories(path: Path): Unit = {
67 +
    if (!JFiles.exists(path))
68 +
      JFiles.createDirectories(path)
69 +
  }
64 70
}

@@ -8,7 +8,6 @@
Loading
8 8
import clairvoyance.io.ClasspathResources
9 9
10 10
import scala.util.Properties.lineSeparator
11 -
import scala.xml.{NodeSeq, Xhtml}
12 11
13 12
trait ClairvoyanceHtmlFileWriter {
14 13
@@ -25,13 +24,13 @@
Loading
25 24
      s"""<?xml version="1.0" encoding="UTF-8"?>
26 25
				 §<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
27 26
				 §
28 -
				 §${Xhtml.toXhtml(file.xml)}""".stripMargin('§').getBytes
27 +
				 §${file.html}""".stripMargin('§').getBytes
29 28
    )
30 29
    if (file.notifyUser)
31 30
      println(s"Output:$lineSeparator${reportFile.toAbsolutePath}")
32 31
  }
33 32
34 -
  protected def writeXml(xml: NodeSeq)(out: Writer): Unit = out.write(Xhtml.toXhtml(xml))
33 +
  protected def writeXml(xml: String)(out: Writer): Unit = out.write(xml)
35 34
36 35
  protected def copyResources(): Unit = copyResourcesOnlyOnceTo(outputDir)
37 36

@@ -20,5 +20,5 @@
Loading
20 20
21 21
  private def writeResult(result: Option[SuiteResult]): Unit =
22 22
    new SingleClairvoyanceHtmlFileWriter()
23 -
      .writeFiles(result.map(new SingleClairvoyanceHtmlPrinter().print).toSeq)
23 +
      .writeFiles(result.map(ClairvoyanceHtmlPrinter.print).toSeq)
24 24
}

@@ -4,93 +4,72 @@
Loading
4 4
import clairvoyance.rendering.Rendering
5 5
import clairvoyance.state.TestState
6 6
import scala.util.Properties.lineSeparator
7 -
import scala.xml.{NodeBuffer, NodeSeq, Unparsed}
8 7
9 -
abstract class HtmlFormat(val xml: NodeSeq) {
10 -
  type Self <: HtmlFormat
8 +
trait HtmlFormat {
11 9
12 -
  def printHtml(n: => NodeSeq): Self = print(<html>{n}</html>)
10 +
  def head(specificationTitle: String): String = {
11 +
    s"""<head>
12 +
    <title>${specificationTitle}</title>
13 +
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
14 +
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js" type="text/javascript"></script>
13 15
14 -
  def printHead(specificationTitle: String): Self = print(xml ++ head(specificationTitle))
16 +
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/themes/base/jquery-ui.css" type="text/css" media="all"/>
17 +
    <link rel="stylesheet" href="http://static.jquery.com/ui/css/demo-docs-theme/ui.theme.css" type="text/css" media="all"/>
18 +
    <link rel="stylesheet" href="css/yatspec.css" type="text/css" media="all"/>
15 19
16 -
  private def head(specificationTitle: String): NodeSeq = {
17 -
    <head>
18 -
      <title>{specificationTitle}</title>
19 -
      <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
20 -
      <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/jquery-ui.min.js" type="text/javascript"></script>
20 +
    <script src="javascript/xregexp.js" type="text/javascript"></script>
21 +
    <script src="javascript/yatspec.js" type="text/javascript"></script>
22 +
    <script src="javascript/sequence_diagram.js" type="text/javascript"></script>
23 +
    <script type="text/javascript">
24 +
       function toggleDetails(contentId, linkId) {
25 +
         var content  = document.getElementById(contentId);
26 +
         var linkText = document.getElementById(linkId);
27 +
         if (content.style.display == \"block\") {
28 +
           content.style.display = \"none\";
29 +
           linkText.innerHTML = \"[ show stacktrace ]\";
30 +
         } else {
31 +
           content.style.display = \"block\";
32 +
           linkText.innerHTML = \"[ hide stacktrace ]\";
33 +
         }
34 +
       }
21 35
22 -
      <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.7/themes/base/jquery-ui.css" type="text/css" media="all"/>
23 -
      <link rel="stylesheet" href="http://static.jquery.com/ui/css/demo-docs-theme/ui.theme.css" type="text/css" media="all"/>
24 -
      <link rel="stylesheet" href="css/yatspec.css" type="text/css" media="all"/>
25 -
26 -
      <script src="javascript/xregexp.js" type="text/javascript"></script>
27 -
      <script src="javascript/yatspec.js" type="text/javascript"></script>
28 -
      <script src="javascript/sequence_diagram.js" type="text/javascript"></script>
29 -
      <script type="text/javascript">{
30 -
      Unparsed(s"""
31 -
         |function toggleDetails(contentId, linkId) {
32 -
         |  var content  = document.getElementById(contentId);
33 -
         |  var linkText = document.getElementById(linkId);
34 -
         |  if (content.style.display == \"block\") {
35 -
         |    content.style.display = \"none\";
36 -
         |    linkText.innerHTML = \"[ show stacktrace ]\";
37 -
         |  } else {
38 -
         |    content.style.display = \"block\";
39 -
         |    linkText.innerHTML = \"[ hide stacktrace ]\";
40 -
         |  }
41 -
         |}
42 -
         |
43 -
         |function hideOpenInNewTabIfRequired() {
44 -
         |  if (top === self) { document.getElementById('printlink').style.display = 'none'; }
45 -
         |}
46 -
         |""".stripMargin)
47 -
    }</script>
48 -
    </head>
36 +
       function hideOpenInNewTabIfRequired() {
37 +
         if (top === self) { document.getElementById('printlink').style.display = 'none'; }
38 +
       }
39 +
  </script>
40 +
</head>"""
49 41
  }
50 42
51 43
  protected def interestingGivensTable(
52 44
      testState: Option[TestState],
53 45
      rendering: Rendering
54 -
  ): Seq[NodeSeq] = {
55 -
    val givens = testState.map(_.interestingGivens).getOrElse(Seq())
56 -
57 -
    givens match {
58 -
      case Nil => NodeSeq.Empty
59 -
      case _ =>
60 -
        <h3 class="logKey">Interesting Givens</h3>
61 -
        <table class="interestingGivens logValue">
62 -
          {mapInterestingGivenRows(givens, rendering)}
63 -
        </table>
64 -
    }
65 -
  }
66 -
67 -
  private def mapInterestingGivenRows(
68 -
      givens: Seq[(String, Any)],
69 -
      rendering: Rendering
70 -
  ): Seq[NodeSeq] = givens.map {
71 -
    case (key: String, value: Any) =>
72 -
      <tr>
73 -
        <th class="key">{key}</th>
74 -
        <td class="interestingGiven">{rendering.renderToXml(value)}</td>
75 -
      </tr>
46 +
  ): String = {
47 +
    testState.toSeq.flatMap(_.interestingGivens).map {
48 +
      case (key: String, value: Any) =>
49 +
        s"""
50 +
          <h3 class="logKey">Interesting Givens</h3>
51 +
          <table class="interestingGivens logValue">
52 +
           <tr>
53 +
              <th class="key">${key}</th>
54 +
              <td class="interestingGiven">${rendering.renderToXml(value)}</td>
55 +
              </tr>
56 +
          </table>"""
57 +
    }.mkString("\n")
76 58
  }
77 59
78 60
  protected def loggedInputsAndOutputs(
79 61
      testState: Option[TestState],
80 62
      rendering: Rendering
81 -
  ): Seq[NodeBuffer] = {
82 -
    val inputsAndOutputs = testState.map(_.capturedInputsAndOutputs).getOrElse(Seq())
83 -
    inputsAndOutputs.map {
63 +
  ): Seq[String] = {
64 +
    testState.toSeq.flatMap(_.capturedInputsAndOutputs).map {
84 65
      case CapturedValue(id, key, value) =>
85 -
        <h3 class="logKey" logkey={id.toString}>{key}</h3>
86 -
        <div class={s"logValue highlight monospace ${value.getClass.getSimpleName}"}>{
87 -
          rendering.renderToXml(value)
88 -
        }</div>
66 +
        s"""
67 +
        <h3 class="logKey" logkey="${id.toString}">${key}</h3>
68 +
        <div class="logValue highlight monospace ${value.getClass.getSimpleName}">${rendering.renderToXml(value)}</div>
69 +
        """
89 70
    }
90 71
  }
91 72
92 -
  protected def print(xml2: NodeSeq): Self
93 -
94 73
  protected def linkNameOf(formattedResultText: String): String =
95 74
    formattedResultText.replaceAll("\\s", "")
96 75

@@ -1,34 +1,33 @@
Loading
1 1
package clairvoyance.rendering
2 2
3 +
import java.net.URLEncoder
4 +
import java.nio.charset.Charset
5 +
3 6
import clairvoyance.plugins.{GraphVizDiagram, SvgSequenceDiagram}
4 -
import scala.xml.{XML, NodeSeq, PrettyPrinter}
5 7
6 8
trait Renderer[T] {
7 -
  def render: (T => Any)
9 +
  def render: T => Any
8 10
}
9 11
10 12
class Rendering(specInstance: Option[CustomRendering]) {
11 13
  val defaultRenderer  = new UnformattedRender
12 -
  val nodeSeqRenderer  = new NodeSeqRenderer
13 14
  val umlRenderer      = new SvgSequenceDiagramRenderer
14 15
  val graphVizRenderer = new GraphVizRenderer
15 16
16 -
  val render = specInstance match {
17 +
  val render: PartialFunction[Any, Any] = specInstance match {
17 18
    case Some(r) => r.customRendering orElse defaultRendering
18 19
    case None    => defaultRendering
19 20
  }
20 21
21 22
  def defaultRendering: PartialFunction[Any, Any] = {
22 -
    case xml: NodeSeq              => nodeSeqRenderer.render(xml)
23 23
    case graphViz: GraphVizDiagram => graphVizRenderer.render(graphViz)
24 24
    case svg: SvgSequenceDiagram   => umlRenderer.render(svg)
25 25
    case any: Any                  => defaultRenderer.render(any)
26 26
  }
27 27
28 -
  def renderToXml(anything: Any) = render(anything) match {
29 -
    case xml: NodeSeq => <div class='nohighlight'>{xml}</div>
30 -
    case Html(html)   => <div class='nohighlight'>{html}</div>
31 -
    case any          => <span>{any.toString}</span>
28 +
  def renderToXml(anything: Any): String = render(anything) match {
29 +
    case Html(html)   => s"<div class='nohighlight'>$html</div>"
30 +
    case any          => s"<span>${any.toString}</span>"
32 31
  }
33 32
}
34 33
@@ -36,18 +35,13 @@
Loading
36 35
  def render = identity
37 36
}
38 37
39 -
class NodeSeqRenderer extends Renderer[NodeSeq] {
40 -
  val formatter = new PrettyPrinter(80, 2)
41 -
  def render    = nodes => formatter.formatNodes(nodes)
42 -
}
43 -
44 38
class SvgSequenceDiagramRenderer extends Renderer[SvgSequenceDiagram] {
45 -
  def render = diagram => XML.loadString(diagram.toMarkup)
39 +
  def render = diagram => Html(diagram.toMarkup)
46 40
}
47 41
48 42
class GraphVizRenderer extends Renderer[GraphVizDiagram] {
49 43
  def render = diagram => {
50 -
    val url = "https://chart.googleapis.com/chart?cht=gv&chl=" + diagram.toMarkup
51 -
    <img src={url}></img>
44 +
    val url = "https://chart.googleapis.com/chart?cht=gv&chl=" + URLEncoder.encode(diagram.toMarkup, Charset.defaultCharset)
45 +
    Html(s"<img src=$url></img>")
52 46
  }
53 47
}
Files Coverage
core/src/main/scala/clairvoyance 73.26%
scalatest/src/main/scala/clairvoyance/scalatest 59.13%
Project Totals (30 files) 66.95%
138.2
TRAVIS_JDK_VERSION=openjdk11
TRAVIS_OS_NAME=linux
138.1
TRAVIS_JDK_VERSION=openjdk11
TRAVIS_OS_NAME=linux

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading