summaryrefslogtreecommitdiff
path: root/examples/scala-js/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala
blob: 2686e313cb4393b11c51eac3582d501b4cd4dfec (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js Test Framework    **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013, LAMP/EPFL        **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-js.org/       **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */


package org.scalajs.jasminetest

import scala.scalajs.js
import scala.scalajs.js.Dynamic.global
import scala.scalajs.js.JavaScriptException
import scala.scalajs.js.annotation.JSExport

import scala.scalajs.testbridge._

import scala.collection.mutable

object JasmineTestFramework extends TestFramework {
  createStackPropertyOnThrowable()

  private def createStackPropertyOnThrowable(): Unit = {
    /* All Jasmine cares about when looking for stack trace data is a field
     * `stack` on the error object. Our Throwables do not have a `stack` field
     * because they are not subclasses of the JavaScript class Error.
     * However, a genuine Error object with the proper (lazy) stack field is
     * stored under the property stackdata by StackTrace.
     * This code installs a property getter on Throwable that will redirect
     * `throwable.stack` to `throwable.stackdata.stack` (when it exists).
     */

    val ThrowablePrototype = js.Object.getPrototypeOf(
        (new Throwable).asInstanceOf[js.Object]).asInstanceOf[js.Object]

    js.Object.defineProperty(ThrowablePrototype, "stack", js.Dynamic.literal(
        configurable = false,
        enumerable = false,
        get = { (self: js.Dynamic) =>
          self.stackdata && self.stackdata.stack
        }: js.ThisFunction
    ).asInstanceOf[js.PropertyDescriptor])
  }

  private val tags = mutable.Set.empty[String]

  def hasTag(tag: String): Boolean = tags.contains(tag)

  def runTest(testOutput: TestOutput, args: js.Array[String])(
    test: js.Function0[Test]): Unit = {

    val jasmine = global.jasmine
    val reporter = new JasmineTestReporter(testOutput)

    handleArgs(args, testOutput)

    try {
      test()

      val jasmineEnv = jasmine.getEnv()
      jasmineEnv.addReporter(reporter.asInstanceOf[js.Any])
      jasmineEnv.updateInterval = 0
      jasmineEnv.execute()
    } catch {
      case throwable@JavaScriptException(exception) =>
        testOutput.error("Problem executing code in tests: " + exception,
            throwable.getStackTrace())
    } finally {
      clearTags()
    }
  }

  /** Set tags used in tests. Only use when invoking with HTML reporter */
  @JSExport
  def setTags(newTags: String*): Unit = {
    clearTags()
    tags ++= newTags
  }

  /** Clear tags used in tests. Only use when invoking with HTML reporter */
  @JSExport
  def clearTags(): Unit = tags.clear()

  private def handleArgs(args: js.Array[String], testOutput: TestOutput) = {
    for (arg <- args) {
      if (arg.startsWith("-t"))
        tags += arg.stripPrefix("-t")
      else
        testOutput.log.warn(s"Jasmine: Discarding argument: $arg")
    }
  }
}