summaryrefslogtreecommitdiff
path: root/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest
diff options
context:
space:
mode:
Diffstat (limited to 'jasmine-test-framework/src/main/scala/org/scalajs/jasminetest')
-rw-r--r--jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala34
-rw-r--r--jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala93
-rw-r--r--jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala130
-rw-r--r--jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala55
4 files changed, 312 insertions, 0 deletions
diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala
new file mode 100644
index 0000000..715d39d
--- /dev/null
+++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTest.scala
@@ -0,0 +1,34 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+import scala.scalajs.js
+import scala.scalajs.testbridge._
+
+import java.util.regex.Pattern
+import org.scalajs.jasmine.Jasmine
+import org.scalajs.jasmine.JasmineExpectation
+
+class JasmineTest extends Test with TestSuiteContext {
+ def jasmine = Jasmine.jasmine
+ def describe(name: String)(suite: => Unit): Unit = Jasmine.describe(name, suite _)
+ def it(title: String)(test: => Unit): Unit = Jasmine.it(title, test _)
+ def xdescribe(name: String)(suite: => Unit): Unit = Jasmine.xdescribe(name, suite _)
+ def xit(title: String)(test: => Unit): Unit = Jasmine.xit(title, test _)
+ def beforeEach(block: => Unit): Unit = Jasmine.beforeEach(block _)
+ def afterEach(block: => Unit): Unit = Jasmine.afterEach(block _)
+ def expect(exp: CharSequence): JasmineExpectation =
+ Jasmine.expect(if (exp == null) null else exp.toString)
+ def expect(exp: js.Any): JasmineExpectation = Jasmine.expect(exp)
+ def runs(block: => Unit): Unit = Jasmine.runs(block _)
+ def waits(timeout: Int): Unit = Jasmine.waits(timeout)
+ def waitsFor(block: => Boolean, errorMsg: String, timeout: Int): Unit =
+ Jasmine.waitsFor(block _, errorMsg, timeout)
+}
diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala
new file mode 100644
index 0000000..2686e31
--- /dev/null
+++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestFramework.scala
@@ -0,0 +1,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")
+ }
+ }
+}
diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala
new file mode 100644
index 0000000..79a7c75
--- /dev/null
+++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/JasmineTestReporter.scala
@@ -0,0 +1,130 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSExport
+
+import scala.scalajs.runtime.StackTrace
+
+import scala.scalajs.testbridge._
+
+import org.scalajs.jasmine.ExpectationResult
+import org.scalajs.jasmine.Result
+import org.scalajs.jasmine.Spec
+import org.scalajs.jasmine.Suite
+
+/** This class is passed to the actual jasmine framework as a reporter */
+class JasmineTestReporter(testOutput: TestOutput) {
+ private var currentSuite: Suite = _
+
+ @JSExport
+ def reportRunnerStarting(): Unit = {
+ testOutput.log.info("")
+ }
+
+ @JSExport
+ def reportSpecStarting(spec: Spec): Unit = {
+ if (currentSuite != spec.suite) {
+ currentSuite = spec.suite
+ info(currentSuite.description)
+ }
+ }
+
+ @JSExport
+ def reportSpecResults(spec: Spec): Unit = {
+ val results = spec.results()
+ val description = spec.description
+
+ if (results.passed) {
+ testOutput.succeeded(s" $success $description")
+ } else {
+ error(s" $failure $description")
+
+ results.getItems foreach displayResult
+ }
+ }
+
+ @JSExport
+ def reportSuiteResults(suite: Suite): Unit = {
+ var results = suite.results()
+
+ info("")
+ val title = "Total for suite " + suite.description
+ val message =
+ s"${results.totalCount} specs, ${results.failedCount} failure"
+
+ if (results.passedCount != results.totalCount) {
+ error(title)
+ errorWithInfoColor(message)
+ } else {
+ info(title)
+ infoWithInfoColor(message)
+ }
+ info("")
+ }
+
+ @JSExport
+ def reportRunnerResults(): Unit = {
+ // no need to report
+ }
+
+ private def info(str: String) =
+ testOutput.log.info(str)
+
+ private def infoWithInfoColor(str: String) =
+ info(withColor(testOutput.infoColor, str))
+
+ private def errorWithInfoColor(str: String) =
+ error(withColor(testOutput.infoColor, str))
+
+ private def error(msg: js.Any) =
+ testOutput.log.error(msg.toString)
+
+ private def withColor(color: testOutput.Color, message: String) =
+ testOutput.color(message, color)
+
+ private def sanitizeMessage(message: String) = {
+ val FilePattern = """^(.+?) [^ ]+\.js \(line \d+\)\.*?$""".r
+ val EvalPattern = """^(.+?) in eval.+\(eval\).+?\(line \d+\).*?$""".r
+
+ message match {
+ case FilePattern(originalMessage) => originalMessage
+ case EvalPattern(originalMessage) => originalMessage
+ case message => message
+ }
+ }
+
+ private def failure = withColor(testOutput.errorColor, "x")
+ private def success = withColor(testOutput.successColor, "+")
+
+ private def displayResult(result: Result) = {
+ (result.`type`: String) match {
+ case "log" =>
+ info(s" ${result.toString}")
+ case "expect" =>
+ val r = result.asInstanceOf[ExpectationResult]
+
+ if (!r.passed()) {
+ val message = sanitizeMessage(r.message)
+ val stack = StackTrace.extract(r.trace).takeWhile { stackElem =>
+ (stackElem.getFileName == null ||
+ !stackElem.getFileName.endsWith("jasmine.js"))
+ }
+
+ if (stack.isEmpty)
+ testOutput.failure(s" $message")
+ else
+ testOutput.error(s" $message", stack)
+ }
+ }
+ }
+
+}
diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala
new file mode 100644
index 0000000..827eefd
--- /dev/null
+++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasminetest/TestSuiteContext.scala
@@ -0,0 +1,55 @@
+/* __ *\
+** ________ ___ / / ___ __ ____ Scala.js Test Framework **
+** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ **
+** /____/\___/_/ |_/____/_/ | |__/ /____/ **
+** |/____/ **
+\* */
+
+
+package org.scalajs.jasminetest
+
+trait TestSuiteContext {
+ def describe(title: String)(test: => Unit): Unit
+ def it(title: String)(test: => Unit): Unit
+ def xdescribe(title: String)(test: => Unit): Unit
+ def xit(title: String)(test: => Unit): Unit
+
+ def when(tag: String): TestSuiteContext =
+ if (JasmineTestFramework.hasTag(tag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def whenAll(tags: String*): TestSuiteContext =
+ if (tags.forall(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def whenAny(tags: String*): TestSuiteContext =
+ if (tags.exists(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def unless(tag: String): TestSuiteContext =
+ if (!JasmineTestFramework.hasTag(tag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def unlessAll(tags: String*): TestSuiteContext =
+ if (!tags.forall(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+
+ def unlessAny(tags: String*): TestSuiteContext =
+ if (!tags.exists(JasmineTestFramework.hasTag)) this
+ else new TestSuiteContext.IgnoredContext(this)
+}
+
+object TestSuiteContext {
+ private class IgnoredContext(
+ baseContext: TestSuiteContext) extends TestSuiteContext {
+ def describe(title: String)(test: => Unit): Unit =
+ baseContext.xdescribe(title)(test)
+ def it(title: String)(test: => Unit): Unit =
+ baseContext.xit(title)(test)
+ def xdescribe(title: String)(test: => Unit): Unit =
+ baseContext.xdescribe(title)(test)
+ def xit(title: String)(test: => Unit): Unit =
+ baseContext.xit(title)(test)
+ }
+}