diff options
Diffstat (limited to 'jasmine-test-framework/src/main/scala/org')
12 files changed, 411 insertions, 0 deletions
diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala new file mode 100644 index 0000000..ca0a63f --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Jasmine.scala @@ -0,0 +1,17 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +object Jasmine extends js.GlobalScope { + def jasmine: JasmineEnv = js.native + def describe(name: String, suite: js.Function0[_]): Unit = js.native + def it(title: String, test: js.Function0[_]): Unit = js.native + def xdescribe(name: String, suite: js.Function0[_]): Unit = js.native + def xit(title: String, test: js.Function0[_]): Unit = js.native + def beforeEach(block: js.Function0[_]): Unit = js.native + def afterEach(block: js.Function0[_]): Unit = js.native + def expect(exp: js.Any): JasmineExpectation = js.native + def runs(block: js.Function0[_]): Unit = js.native + def waits(timeout: Int): Unit = js.native + def waitsFor(block: js.Function0[Boolean], errorMsg: String, timeout: Int): Unit = js.native +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala new file mode 100644 index 0000000..b81121e --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineEnv.scala @@ -0,0 +1,14 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait JasmineEnv extends js.Object { + def Clock: JasmineEnv.Clock = js.native +} + +object JasmineEnv { + trait Clock extends js.Object { + def tick(time: Double): Unit = js.native + def useMock(): Unit = js.native + } +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala new file mode 100644 index 0000000..9aad02e --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/JasmineExpectation.scala @@ -0,0 +1,21 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait JasmineExpectation extends js.Object { + def toBe(exp: js.Any): Unit = js.native + def toEqual(exp: js.Any): Unit = js.native + def toMatch(exp: js.RegExp): Unit = js.native + def toMatch(exp: String): Unit = js.native + def toBeDefined(): Unit = js.native + def toBeUndefined(): Unit = js.native + def toBeNull(): Unit = js.native + def toBeTruthy(): Unit = js.native + def toBeFalsy(): Unit = js.native + def toContain(exp: js.Any): Unit = js.native + def toBeGreaterThan(exp: Double): Unit = js.native + def toBeLessThan(exp: Double): Unit = js.native + def toBeCloseTo(exp: Double, precision: Int = 2): Unit = js.native + def toThrow(): Unit = js.native + val not: JasmineExpectation = js.native +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala new file mode 100644 index 0000000..aed78b9 --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Results.scala @@ -0,0 +1,13 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait Result extends js.Object { + def `type`: String = js.native + val trace: js.Dynamic = js.native +} + +trait ExpectationResult extends Result { + def passed(): Boolean = js.native + val message: String = js.native +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala new file mode 100644 index 0000000..afbfa13 --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Spec.scala @@ -0,0 +1,9 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait Spec extends js.Object { + def results(): SpecResults = js.native + val description: String = js.native + val suite: Suite = js.native +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala new file mode 100644 index 0000000..50f073c --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SpecResults.scala @@ -0,0 +1,8 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait SpecResults extends js.Object { + def passed(): Boolean = js.native + def getItems(): js.Array[Result] = js.native +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala new file mode 100644 index 0000000..cd4fb75 --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/Suite.scala @@ -0,0 +1,8 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait Suite extends js.Object { + def results(): SuiteResults = js.native + val description: String = js.native +} diff --git a/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala new file mode 100644 index 0000000..db98acf --- /dev/null +++ b/jasmine-test-framework/src/main/scala/org/scalajs/jasmine/SuiteResults.scala @@ -0,0 +1,9 @@ +package org.scalajs.jasmine + +import scala.scalajs.js + +trait SuiteResults extends js.Object { + val passedCount: Int = js.native + val failedCount: Int = js.native + val totalCount: Int = js.native +} 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) + } +} |