diff options
Diffstat (limited to 'examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop')
11 files changed, 2115 insertions, 0 deletions
diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala new file mode 100644 index 0000000..2ffd6b7 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ArrayTest.scala @@ -0,0 +1,94 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +object ArrayTest extends JasmineTest { + + describe("scala.scalajs.js.Array") { + + it("should provide implicit conversion from js.Array to ArrayOps - String") { + var propCount = 0 + var propString = "" + + for (item <- js.Array("Sc", "ala", ".", "js")) { + expect(item.isInstanceOf[String]).toBeTruthy + propCount += 1 + propString += item + } + + expect(propCount).toEqual(4) + expect(propString).toEqual("Scala.js") + } + + it("should provide implicit conversion from js.Array to ArrayOps - Int") { + var propCount = 0 + var propString = "" + + for (item <- js.Array(7, 3, 5, 7)) { + expect(item.isInstanceOf[Int]).toBeTruthy + propCount += 1 + propString += item + } + + expect(propCount).toEqual(4) + expect(propString).toEqual("7357") + } + + it("should provide implicit conversion from js.Array to ArrayOps - Char") { + var propCount = 0 + var propString = "" + + for (item <- js.Array('S', 'c', 'a', 'l', 'a')) { + expect(item.isInstanceOf[Char]).toBeTruthy + propCount += 1 + propString += item + } + + expect(propCount).toEqual(5) + expect(propString).toEqual("Scala") + } + + it("should provide implicit conversion from js.Array to ArrayOps - value class") { + var propCount = 0 + var propString = "" + + for (item <- js.Array(new VC(5), new VC(-4))) { + expect(item.isInstanceOf[VC]).toBeTruthy + propCount += 1 + propString += item + } + + expect(propCount).toEqual(2) + expect(propString).toEqual("VC(5)VC(-4)") + } + + } + + describe("scala.scalajs.js.JSConverters.JSRichGenTraversableOnce") { + + import js.JSConverters._ + + it("should provide toJSArray") { + expect(List("foo", "bar").toJSArray).toEqual(js.Array("foo", "bar")) + expect(Iterator(1, 2, 3).toJSArray).toEqual(js.Array(1, 2, 3)) + expect(Array(0.3, 7.3, 8.9).toJSArray).toEqual(js.Array(0.3, 7.3, 8.9)) + expect(None.toJSArray).toEqual(js.Array()) + // The following fails on 2.10.x + //expect(Some("Hello World").toJSArray).toEqual(js.Array("Hello World")) + } + + } + + private class VC(val x: Int) extends AnyVal { + override def toString(): String = s"VC($x)" + } + +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala new file mode 100644 index 0000000..e37c89e --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/AsyncTest.scala @@ -0,0 +1,121 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import scala.scalajs.js.JSConverters._ + +import org.scalajs.jasminetest.JasmineTest + +import scala.concurrent.{Future, ExecutionContext} +import scala.scalajs.concurrent.JSExecutionContext + +import scala.collection.mutable.ArrayBuffer + +import org.scalajs.jasmine.JasmineExpectation + +object AsyncTest extends JasmineTest { + + def asyncTest(implicit executor: ExecutionContext) = { + val steps = new ArrayBuffer[String] + + steps += "prep-future" + + val f1 = Future { + steps += "future" + 1 + 2 + 3 + } + + steps += "prep-map" + + val f2 = f1 map { x => + steps += "map" + x * 2 + } + + steps += "prep-foreach" + + f2 foreach { _ => steps += "foreach" } + + steps += "done" + + steps + } + + def expect(abuf: ArrayBuffer[String]): JasmineExpectation = + expect(abuf.toJSArray) + + describe("scala.scalajs.concurrent.JSExecutionContext.queue") { + + beforeEach { + jasmine.Clock.useMock() + } + + it("should correctly order future calls") { + val res = asyncTest(JSExecutionContext.queue) + + expect(res).toEqual(js.Array( + "prep-future", + "prep-map", + "prep-foreach", + "done")) + + jasmine.Clock.tick(1) + + expect(res).toEqual(js.Array( + "prep-future", + "prep-map", + "prep-foreach", + "done", + "future", + "map", + "foreach")) + } + + } + + describe("scala.scalajs.concurrent.JSExecutionContext.runNow") { + + it("should correctly order future calls") { + val res = asyncTest(JSExecutionContext.runNow) + + expect(res).toEqual(js.Array( + "prep-future", + "future", + "prep-map", + "map", + "prep-foreach", + "foreach", + "done")) + } + + } + + describe("scala.concurrent.Future") { + + it("should support map") { + implicit val ec = JSExecutionContext.runNow + val f = Future(3).map(x => x*2) + expect(f.value.get.get).toEqual(6) + } + + it("should support flatMap") { + implicit val ec = JSExecutionContext.runNow + val f = Future(Future(3)).flatMap(x => x) + expect(f.value.get.get).toEqual(3) + } + + it("should support sequence") { + implicit val ec = JSExecutionContext.runNow + val f = Future.sequence(Seq(Future(3), Future(5))) + expect(f.value.get.get.toJSArray).toEqual(js.Array(3, 5)) + } + + } + +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala new file mode 100644 index 0000000..8b45395 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DictionaryTest.scala @@ -0,0 +1,79 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +object DictionaryTest extends JasmineTest { + + describe("scala.scalajs.js.Dictionary") { + + it("should provide an equivalent of the JS delete keyword - #255") { + val obj = js.Dictionary.empty[js.Any] + obj("foo") = 42 + obj("bar") = "foobar" + + expect(obj("foo")).toEqual(42) + expect(obj("bar")).toEqual("foobar") + obj.delete("foo") + expect(obj("foo")).toBeUndefined + expect(obj.asInstanceOf[js.Object].hasOwnProperty("foo")).toBeFalsy + expect(obj("bar")).toEqual("foobar") + } + + // This doesn't work on Rhino due to lack of full strict mode support - #679 + unless("rhino"). + it("should behave as specified when deleting a non-configurable property - #461 - #679") { + val obj = js.Dictionary.empty[js.Any] + js.Object.defineProperty(obj.asInstanceOf[js.Object], "nonconfig", + js.Dynamic.literal(value = 4, writable = false).asInstanceOf[js.PropertyDescriptor]) + expect(obj("nonconfig")).toEqual(4) + expect(() => obj.delete("nonconfig")).toThrow + expect(obj("nonconfig")).toEqual(4) + } + + it("should provide `get`") { + val obj = js.Dictionary.empty[Int] + obj("hello") = 1 + + expect(obj.get("hello").isDefined).toBeTruthy + expect(obj.get("world").isDefined).toBeFalsy + } + + it("should treat delete as a statement - #907") { + val obj = js.Dictionary("a" -> "A") + obj.delete("a") + } + + it("should desugar arguments to delete statements - #908") { + val kh = js.Dynamic.literal( key = "a" ).asInstanceOf[KeyHolder] + val dict = js.Dictionary[String]("a" -> "A") + def a[T](foo: String) = dict.asInstanceOf[T] + a[js.Dictionary[String]]("foo").delete(kh.key) + } + + } + + trait KeyHolder extends js.Object { + def key: String = js.native + } + + describe("scala.scalajs.js.JSConverters.JSRichGenMap") { + + import js.JSConverters._ + + it("should provide toJSDictionary") { + expect(Map("a" -> 1, "b" -> 2).toJSDictionary).toEqual( + js.Dynamic.literal(a = 1, b = 2)) + expect(Map("a" -> "foo", "b" -> "bar").toJSDictionary).toEqual( + js.Dynamic.literal(a = "foo", b = "bar")) + } + + } +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala new file mode 100644 index 0000000..2b6942f --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/DynamicTest.scala @@ -0,0 +1,187 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +import js.annotation.JSExport + +object DynamicTest extends JasmineTest { + + describe("scala.scalajs.js.Dynamic") { + + it("should workaround Scala 2.10 issue with implicit conversion for dynamic fields named x - #8") { + class Point(val x: Int, val y: Int) + + def jsonToPoint(json: js.Dynamic) = { + new Point(json.x.toString.toInt, json.y.toString.toInt) + } + + val json = js.eval("var dynamicTestPoint = { x: 1, y: 2 }; dynamicTestPoint;") + val point = jsonToPoint(json.asInstanceOf[js.Dynamic]) + + expect(point.x).toEqual(1) + expect(point.y).toEqual(2) + } + + it("should allow to call functions with arguments named x") { + class A { + def a = 1 + } + + class B extends A { + @JSExport + def x(par: Int) = a + par // make sure `this` is bound correctly in JS + } + + val b = (new B).asInstanceOf[js.Dynamic] + + expect(b.x(10)).toEqual(11) + } + + it("should allow instanciating JS classes dynamically - #10") { + val DynamicTestClass = js.eval(""" + var DynamicTestClass = function(x) { + this.x = x; + }; + DynamicTestClass; + """).asInstanceOf[js.Dynamic] + val obj = js.Dynamic.newInstance(DynamicTestClass)("Scala.js") + expect(obj.x).toEqual("Scala.js") + } + + it("should allow instantiating JS classes dynamically with varargs - #708") { + val DynamicTestClassVarArgs = js.eval(""" + var DynamicTestClassVarArgs = function() { + this.count = arguments.length; + for (var i = 0; i < arguments.length; i++) + this['elem'+i] = arguments[i]; + }; + DynamicTestClassVarArgs; + """).asInstanceOf[js.Dynamic] + + val obj1 = js.Dynamic.newInstance(DynamicTestClassVarArgs)("Scala.js") + expect(obj1.count).toEqual(1) + expect(obj1.elem0).toEqual("Scala.js") + + val obj2 = js.Dynamic.newInstance(DynamicTestClassVarArgs)( + "Scala.js", 42, true) + expect(obj2.count).toEqual(3) + expect(obj2.elem0).toEqual("Scala.js") + expect(obj2.elem1).toEqual(42) + expect(obj2.elem2).toEqual(true) + + def obj3Args: Seq[js.Any] = Seq("Scala.js", 42, true) + val obj3 = js.Dynamic.newInstance(DynamicTestClassVarArgs)(obj3Args: _*) + expect(obj3.count).toEqual(3) + expect(obj3.elem0).toEqual("Scala.js") + expect(obj3.elem1).toEqual(42) + expect(obj3.elem2).toEqual(true) + } + + it("should provide an object literal construction") { + import js.Dynamic.{ literal => obj } + val x = obj(foo = 3, bar = "foobar") + expect(x.foo).toEqual(3) + expect(x.bar).toEqual("foobar") + expect(x.unknown).toBeUndefined() + + val y = obj( + inner = obj(name = "inner obj"), + fun = { () => 42 } + ) + expect(y.inner.name).toEqual("inner obj") + expect(y.fun()).toEqual(42) + + expect(obj().anything).toBeUndefined() + } + + it("should provide object literal construction with dynamic naming") { + import js.Dynamic.{ literal => obj } + val x = obj("foo" -> 3, "bar" -> "foobar") + expect(x.foo).toEqual(3) + expect(x.bar).toEqual("foobar") + expect(x.unknown).toBeUndefined() + + val tup1 = ("hello1", 3: js.Any) + val tup2 = ("hello2", 10: js.Any) + + val y = obj(tup1, tup2) + expect(y.hello1).toEqual(3) + expect(y.hello2).toEqual(10) + + var count = 0 + val z = obj({ count += 1; ("foo", "bar")}) + expect(z.foo).toEqual("bar") + expect(count).toEqual(1) + } + + it("should allow to create an empty object with the literal syntax") { + import js.Dynamic.{ literal => obj } + val x = obj() + expect(x.isInstanceOf[js.Object]).toBeTruthy() + } + + it("should properly encode object literal property names") { + import js.Dynamic.{ literal => obj } + + val obj0 = obj("3-" -> 42) + expect(obj0.`3-`).toEqual(42) + + val obj0Dict = obj0.asInstanceOf[js.Dictionary[js.Any]] + expect(obj0Dict("3-")).toEqual(42) + + val checkEvilProperties = js.eval(""" + function dynamicLiteralNameEncoding_checkEvilProperties(x) { + return x['.o[3√!|-pr()per7:3$];'] === ' such eval '; + } + dynamicLiteralNameEncoding_checkEvilProperties + """).asInstanceOf[js.Function1[js.Any, Boolean]] + val obj1 = obj( + ".o[3√!|-pr()per7:3$];" -> " such eval ").asInstanceOf[js.Dictionary[js.Any]] + expect(obj1(".o[3√!|-pr()per7:3$];")).toEqual(" such eval ") + expect(checkEvilProperties(obj1)).toEqual(true) + + val checkQuotesProperty = js.eval(""" + function dynamicLiteralNameEncoding_quote(x) { + return x["'" + '"'] === 7357; + } + dynamicLiteralNameEncoding_quote + """).asInstanceOf[js.Function1[js.Any, Boolean]] + + val quote = '"' + + Seq( + obj("'" + quote -> 7357), + obj(s"'$quote" -> 7357), + obj("'\"" -> 7357), + obj("'" + quote -> 7357) + ).foreach { o => + val dict = o.asInstanceOf[js.Dictionary[js.Any]] + expect(dict("'\"")).toEqual(7357) + expect(dict("'" + quote)).toEqual(7357) + expect(dict(s"'$quote")).toEqual(7357) + expect(checkQuotesProperty(o)).toEqual(true) + } + } + + it("should return subclasses of js.Object in literal construction - #783") { + import js.Dynamic.{ literal => obj } + + val a: js.Object = obj(theValue = 1) + expect(a.hasOwnProperty("theValue")).toBeTruthy + expect(a.hasOwnProperty("noValue")).toBeFalsy + + val b: js.Object = obj("theValue" -> 2) + expect(b.hasOwnProperty("theValue")).toBeTruthy + expect(b.hasOwnProperty("noValue")).toBeFalsy + + } + } +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala new file mode 100644 index 0000000..d577d8d --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala @@ -0,0 +1,1075 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import js.annotation._ +import org.scalajs.jasminetest.{JasmineTest, JasmineTestFramework} + +import scala.annotation.meta + +object ExportsTest extends JasmineTest { + + /** This package in the JS (export) namespace */ + val jsPackage = js.Dynamic.global.scala.scalajs.testsuite.jsinterop + + describe("@JSExport") { + + it("should offer exports for methods with implicit name") { + class Foo { + @JSExport + def bar(): Int = 42 + @JSExport + def double(x: Int): Int = x*2 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.bar)).toBe("function") + expect(foo.bar()).toEqual(42) + expect(foo.double(3)).toEqual(6) + } + + it("should offer exports for methods with explicit name") { + class Foo { + @JSExport("theAnswer") + def bar(): Int = 42 + @JSExport("doubleTheParam") + def double(x: Int): Int = x*2 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(foo.bar).toBeUndefined + expect(js.typeOf(foo.theAnswer)).toBe("function") + expect(foo.theAnswer()).toEqual(42) + expect(foo.doubleTheParam(3)).toEqual(6) + } + + it("should offer exports for methods with constant folded name") { + class Foo { + @JSExport(ExportNameHolder.methodName) + def bar(): Int = 42 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(foo.bar).toBeUndefined + expect(foo.myMethod()).toEqual(42) + } + + it("should offer exports for protected methods") { + class Foo { + @JSExport + protected def bar(): Int = 42 + + @JSExport + protected[testsuite] def foo(): Int = 100 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.bar)).toBe("function") + expect(foo.bar()).toEqual(42) + expect(js.typeOf(foo.foo)).toBe("function") + expect(foo.foo()).toEqual(100) + } + + it("should offer exports for properties with implicit name") { + class Foo { + private[this] var myY: String = "hello" + @JSExport + val answer: Int = 42 + @JSExport + var x: Int = 3 + @JSExport + def doubleX: Int = x*2 + @JSExport + def y: String = myY + " get" + @JSExport + def y_=(v: String): Unit = myY = v + " set" + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.answer)).toBe("number") + expect(foo.answer).toEqual(42) + expect(foo.x).toEqual(3) + expect(foo.doubleX).toEqual(6) + foo.x = 23 + expect(foo.x).toEqual(23) + expect(foo.doubleX).toEqual(46) + expect(foo.y).toEqual("hello get") + foo.y = "world" + expect(foo.y).toEqual("world set get") + } + + it("should offer exports for properties with explicit name") { + class Foo { + private[this] var myY: String = "hello" + @JSExport("answer") + val answerScala: Int = 42 + @JSExport("x") + var xScala: Int = 3 + @JSExport("doubleX") + def doubleXScala: Int = xScala*2 + @JSExport("y") + def yGetter: String = myY + " get" + @JSExport("y") + def ySetter_=(v: String): Unit = myY = v + " set" + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(foo.answerScala).toBeUndefined + expect(js.typeOf(foo.answer)).toBe("number") + expect(foo.answer).toEqual(42) + expect(foo.x).toEqual(3) + expect(foo.doubleX).toEqual(6) + foo.x = 23 + expect(foo.x).toEqual(23) + expect(foo.doubleX).toEqual(46) + expect(foo.y).toEqual("hello get") + foo.y = "world" + expect(foo.y).toEqual("world set get") + } + + it("should offer exports for protected properties") { + class Foo { + @JSExport + protected val x: Int = 42 + @JSExport + protected[testsuite] val y: Int = 43 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(foo.x).toEqual(42) + expect(foo.y).toEqual(43) + } + + it("should offer overloaded exports for methods") { + class Foo { + @JSExport("foobar") + def foo(): Int = 42 + @JSExport("foobar") + def bar(x: Int): Int = x*2 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.foobar)).toBe("function") + expect(foo.foobar()).toEqual(42) + expect(foo.foobar(3)).toEqual(6) + } + + it("should offer multiple exports for the same method") { + class Foo { + @JSExport + @JSExport("b") + @JSExport("c") + def a(): Int = 1 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.a)).toBe("function") + expect(js.typeOf(foo.b)).toBe("function") + expect(js.typeOf(foo.c)).toBe("function") + + expect(foo.a()).toEqual(1) + expect(foo.b()).toEqual(1) + expect(foo.c()).toEqual(1) + } + + it("should inherit exports from traits") { + trait Foo { + @JSExport + def x: Int + + @JSExport + def method(x: Int): Int + } + + class Bar extends Foo { + val x = 1 + def method(x: Int) = 2 * x + } + + val bar = (new Bar).asInstanceOf[js.Dynamic] + expect(bar.x).toEqual(1) + expect(js.typeOf(bar.method)).toBe("function") + expect(bar.method(2)).toEqual(4) + } + + it("should offer overloading with inherited exports") { + class A { + @JSExport + def foo(x: Int) = 2*x + } + + class B extends A{ + @JSExport("foo") + def bar(x: String) = s"Hello $x" + } + + val b = (new B).asInstanceOf[js.Dynamic] + expect(js.typeOf(b.foo)).toBe("function") + expect(b.foo(1)).toEqual(2) + expect(b.foo("World")).toEqual("Hello World") + } + + it("should offer exports for generic methods") { + class Foo { + @JSExport + def gen[T <: AnyRef](x: T) = x + } + + val x = (new Object).asInstanceOf[js.Any] + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.gen)).toBe("function") + expect(foo.gen(x)).toBe(x) + } + + it("should offer exports for lambda return types") { + class Foo { + @JSExport + def lambda(x: Int) = (y: Int) => x + y + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.lambda)).toBe("function") + + val lambda = foo.lambda(5).asInstanceOf[Function1[Int,Int]] + + expect(lambda(4)).toEqual(9) + } + + it("should offer exports for multi parameter lists") { + class Foo { + @JSExport + def multiParam(x: Int)(y: Int): Int = x + y + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.multiParam)).toBe("function") + expect(foo.multiParam(5,6)).toEqual(11) + } + + it("should offer exports for default arguments") { + class Foo { + @JSExport + def defArg(x: Int = 1) = x + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.defArg)).toBe("function") + expect(foo.defArg(5)).toEqual(5) + } + + it("should offer exports for weird stuff") { + class UhOh { + // Something no one should export + @JSExport + def ahem[T : Comparable](x: T)(implicit y: Int) = ??? + } + + val x = (new UhOh).asInstanceOf[js.Dynamic] + expect(js.typeOf(x.ahem)).toBe("function") + } + + it("should offer exports with value class return types") { + class Foo { + @JSExport + def vc(x: Int) = new SomeValueClass(x) + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.vc)).toBe("function") + + // The result should be a boxed SomeValueClass + val result = foo.vc(5) + expect(js.typeOf(result)).toEqual("object") + expect((result: Any).isInstanceOf[SomeValueClass]).toBeTruthy + expect((result: Any) == (new SomeValueClass(5))).toBeTruthy + } + + it("should allow exports with Any as return type") { + class A + class Foo { + @JSExport + def foo(switch: Boolean): Any = + if (switch) 1 else new A + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(foo.foo(true).isInstanceOf[Int]).toBeTruthy + expect(foo.foo(false).isInstanceOf[A]).toBeTruthy + } + + it("should accept boxed value classes as parameter") { + class Foo { + @JSExport + def vc(x: SomeValueClass) = x.i + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(js.typeOf(foo.vc)).toBe("function") + + // The parameter should be a boxed SomeValueClass + val valueCls = new SomeValueClass(7) + val result = foo.vc(valueCls.asInstanceOf[js.Any]) + expect(js.typeOf(result)).toEqual("number") + expect(result).toEqual(7) + } + + it("should offer exports for overridden methods with refined return type") { + class A + class B extends A + + class C1 { + @JSExport + def x: A = new A + } + + class C2 extends C1 { + override def x: B = new B + } + + val c2 = (new C2).asInstanceOf[js.Dynamic] + expect(c2.x.isInstanceOf[B]).toBeTruthy + } + + it("should offer exports for methods with refined types as return type") { + class A { + @JSExport + def foo(x: String): js.Object with js.Dynamic = + js.Dynamic.literal(arg = x) + } + + val a = (new A).asInstanceOf[js.Dynamic] + expect(a.foo("hello")).toEqual(js.Dynamic.literal(arg = "hello")) + } + + it("should offer exports for variable argument methods - #393") { + class A { + @JSExport + def foo(i: String*) = i.mkString("|") + } + + val a = (new A).asInstanceOf[js.Dynamic] + + expect(a.foo()).toEqual("") + expect(a.foo("a", "b", "c")).toEqual("a|b|c") + expect(a.foo("a", "b", "c", "d")).toEqual("a|b|c|d") + } + + it("should correctly overload in view of difficult repeated parameter lists") { + class A { + @JSExport + def foo(a: String, b: String, i: Int, c: String) = 1 + + @JSExport + def foo(a: String*) = 2 + + @JSExport + def foo(x: Int)(a: Int*) = x * 100000 + a.sum + } + + val a = (new A).asInstanceOf[js.Dynamic] + + expect(a.foo()).toEqual(2) + expect(a.foo("asdf")).toEqual(2) + expect(a.foo("asdf", "foo")).toEqual(2) + expect(a.foo("asdf", "foo", "bar")).toEqual(2) + expect(a.foo("asdf", "foo", 1, "bar")).toEqual(1) + expect(a.foo("asdf", "foo", "foo", "bar")).toEqual(2) + expect(a.foo(5, 1, 2, 3, 10)).toEqual(500016) + expect(a.foo(1)).toEqual(100000) + } + + it("should offer exports with default arguments") { + class A { + var oneCount: Int = 0 + def one = { + oneCount += 1 + 1 + } + @JSExport + def foo(a: Int = one)(b: Int = a + one)(c: Int = b + one) = + a + b + c + } + + val a = new A + val jsa = a.asInstanceOf[js.Dynamic] + + expect(jsa.foo()).toEqual(6) + expect(a.oneCount).toEqual(3) + + expect(jsa.foo(2)).toEqual(9) + expect(a.oneCount).toEqual(5) + + expect(jsa.foo(2,4)).toEqual(11) + expect(a.oneCount).toEqual(6) + + expect(jsa.foo(2,4,10)).toEqual(16) + expect(a.oneCount).toEqual(6) + + expect(jsa.foo((),4,10)).toEqual(15) + expect(a.oneCount).toEqual(7) + + expect(jsa.foo((),4)).toEqual(10) + expect(a.oneCount).toEqual(9) + } + + it("should correctly overload methods in presence of default parameters") { + class A { + @JSExport + def foo(a: Int)(b: Int = 5)(c: Int = 7) = 1000 + a + b + c + + @JSExport + def foo(a: Int, b: String) = 2 + + @JSExport + def foo(a: Int, b: Int, c: String) = 3 + } + + val a = (new A).asInstanceOf[js.Dynamic] + + expect(a.foo(1)).toEqual(1013) + expect(a.foo(1, 4)).toEqual(1012) + expect(a.foo(1, 4, 5)).toEqual(1010) + expect(a.foo(1, "foo")).toEqual(2) + expect(a.foo(1, 2, "foo")).toEqual(3) + + } + + it("should prefer overloads taking a js.Undefined over methods with default parameters") { + class A { + @JSExport + def foo(a: Int)(b: String = "asdf") = s"$a $b" + + @JSExport + def foo(a: Int, b: js.prim.Undefined) = "woot" + } + + val a = (new A).asInstanceOf[js.Dynamic] + + expect(a.foo(1)).toEqual("1 asdf") + expect(a.foo(2, "omg")).toEqual("2 omg") + expect(a.foo(1, ())).toEqual("woot") + + } + + it("should correctly overload methods in presence of default parameters and repeated parameters") { + class A { + @JSExport + def foo(x: Int, y: Int = 1) = x + y + @JSExport + def foo(x: String*) = x.mkString("|") + } + + val a = (new A).asInstanceOf[js.Dynamic] + + expect(a.foo(1)).toEqual(2) + expect(a.foo(1, 2)).toEqual(3) + expect(a.foo()).toEqual("") + expect(a.foo("foo")).toEqual("foo") + expect(a.foo("foo","bar")).toEqual("foo|bar") + + } + + it("should correctly overload exports called `toString`") { + class A { + override def toString(): String = "no arg" + @JSExport + def toString(x: Int): String = s"with arg: $x" + } + + val a = (new A).asInstanceOf[js.Dynamic] + expect(a.applyDynamic("toString")()).toEqual("no arg") + expect(a.applyDynamic("toString")(1)).toEqual("with arg: 1") + } + + it("should allow to explicitly export toString") { + class A { + @JSExport("toString") + override def toString(): String = "called" + } + + val a = (new A).asInstanceOf[js.Dynamic] + expect(a.applyDynamic("toString")()).toEqual("called") + } + + it("should correctly box repeated parameter lists with value classes") { + class A { + @JSExport + def foo(vcs: SomeValueClass*) = vcs.map(_.i).sum + } + + val vc1 = new SomeValueClass(1) + val vc2 = new SomeValueClass(2) + val a = (new A).asInstanceOf[js.Dynamic] + + expect(a.foo(vc1.asInstanceOf[js.Any], vc2.asInstanceOf[js.Any])).toEqual(3) + } + + it("should offer exports for objects with implicit name") { + val accessor = jsPackage.ExportedObject + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(js.typeOf(obj)).toEqual("object") + expect(obj.witness).toEqual("witness") + } + + it("should offer exports for objects with explicit name") { + val accessor = js.Dynamic.global.TheExportedObject + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(js.typeOf(obj)).toEqual("object") + expect(obj.witness).toEqual("witness") + } + + it("should offer exports for objects with qualified name") { + val accessor = js.Dynamic.global.qualified.testobject.ExportedObject + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(js.typeOf(obj)).toEqual("object") + expect(obj.witness).toEqual("witness") + } + + it("should offer exports for objects with constant folded name") { + val accessor = js.Dynamic.global.ConstantFoldedObjectExport + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(js.typeOf(obj)).toEqual("object") + expect(obj.witness).toEqual("witness") + } + + it("should offer exports for protected objects") { + val accessor = jsPackage.ProtectedExportedObject + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(js.typeOf(obj)).toEqual("object") + expect(obj.witness).toEqual("witness") + } + + it("should offer exports for classes with implicit name") { + val constr = jsPackage.ExportedClass + expect(constr).toBeDefined + expect(js.typeOf(constr)).toEqual("function") + val obj = js.Dynamic.newInstance(constr)(5) + expect(obj.x).toEqual(5) + } + + it("should offer exports for classes with explicit name") { + val constr = js.Dynamic.global.TheExportedClass + expect(constr).toBeDefined + expect(js.typeOf(constr)).toEqual("function") + val obj = js.Dynamic.newInstance(constr)(5) + expect(obj.x).toEqual(5) + } + + it("should offer exports for classes with qualified name") { + val constr = js.Dynamic.global.qualified.testclass.ExportedClass + expect(constr).toBeDefined + expect(js.typeOf(constr)).toEqual("function") + val obj = js.Dynamic.newInstance(constr)(5) + expect(obj.x).toEqual(5) + } + + it("should offer exports for classes with constant folded name") { + val constr = js.Dynamic.global.ConstantFoldedClassExport + expect(constr).toBeDefined + expect(js.typeOf(constr)).toEqual("function") + val obj = js.Dynamic.newInstance(constr)(5) + expect(obj.x).toEqual(5) + } + + it("should offer exports for protected classes") { + val constr = jsPackage.ProtectedExportedClass + expect(constr).toBeDefined + expect(js.typeOf(constr)).toEqual("function") + val obj = js.Dynamic.newInstance(constr)(5) + expect(obj.x).toEqual(5) + } + + it("should offer export for classes with repeated parameters in ctor") { + val constr = jsPackage.ExportedVarArgClass + expect(js.Dynamic.newInstance(constr)().result).toEqual("") + expect(js.Dynamic.newInstance(constr)("a").result).toEqual("a") + expect(js.Dynamic.newInstance(constr)("a", "b").result).toEqual("a|b") + expect(js.Dynamic.newInstance(constr)("a", "b", "c").result).toEqual("a|b|c") + expect(js.Dynamic.newInstance(constr)(5, "a").result).toEqual("Number: <5>|a") + } + + it("should offer export for classes with default parameters in ctor") { + val constr = jsPackage.ExportedDefaultArgClass + expect(js.Dynamic.newInstance(constr)(1,2,3).result).toEqual(6) + expect(js.Dynamic.newInstance(constr)(1).result).toEqual(106) + expect(js.Dynamic.newInstance(constr)(1,2).result).toEqual(103) + } + + it("should correctly disambiguate overloads involving longs") { + + class Foo { + @JSExport + def foo(x: Int) = 1 + @JSExport + def foo(x: Long) = 2 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + + // Create a long factory we can call dynamically to retrieve an unboxed + // long which is typed as a js.Any + object LongFactory { + @JSExport + def aLong = 1L + } + val trueJsLong = LongFactory.asInstanceOf[js.Dynamic].aLong + + expect(foo.foo(1)).toEqual(1) + expect(foo.foo(trueJsLong)).toEqual(2) + } + + it("should return boxed Chars") { + class Foo { + @JSExport + def bar(x: Int): Char = x.toChar + } + val foo = (new Foo).asInstanceOf[js.Dynamic] + + val funs = js.eval(""" + var funs = { + testIsChar: function(foo) { return JSUtils().isChar(foo.bar(65)); }, + testCharValue: function(foo) { return JSUtils().charToString(foo.bar(65)); } + }; funs; + """).asInstanceOf[js.Dynamic] + + expect(funs.testIsChar(foo)).toBeTruthy + expect(funs.testCharValue(foo)).toEqual("A") + } + + it("should take boxed Chars as parameter") { + class Foo { + @JSExport + def bar(x: Char): Int = x.toInt + } + val foo = (new Foo).asInstanceOf[js.Dynamic] + + val f = js.eval(""" + var f = function(foo) { return foo.bar(JSUtils().stringToChar('e')); }; + f; + """).asInstanceOf[js.Dynamic] + + expect(f(foo)).toEqual('e'.toInt) + } + + it("should be able to disambiguate an Int from a Char") { + class Foo { + @JSExport + def bar(x: Char): String = "char: "+x + @JSExport + def bar(x: Int): String = "int: "+x + } + val foo = (new Foo).asInstanceOf[js.Dynamic] + + val funs = js.eval(""" + var funs = { + testChar: function(foo) { return foo.bar(JSUtils().stringToChar('S')); }, + testInt: function(foo) { return foo.bar(68); } + }; funs; + """).asInstanceOf[js.Dynamic] + + expect(funs.testChar(foo)).toEqual("char: S") + expect(funs.testInt(foo)).toEqual("int: 68") + } + + it("should support exporting constructor parameter fields - #970") { + class Foo(@(JSExport @meta.field) val x: Int) + val foo = (new Foo(1)).asInstanceOf[js.Dynamic] + expect(foo.x).toEqual(1) + } + + it("should support exporting case class fields - #970") { + case class Foo(@(JSExport @meta.field) x: Int) + val foo = (new Foo(1)).asInstanceOf[js.Dynamic] + expect(foo.x).toEqual(1) + } + + it("should support exporting lazy values - #977") { + class Foo { + @JSExport + lazy val x = 1 + } + val foo = (new Foo).asInstanceOf[js.Dynamic] + expect(foo.x).toEqual(1) + } + + it("should support exporting all members of a class") { + @JSExportAll + class Foo { + val a = 1 + + @JSExport // double annotation allowed + def b = 2 + + lazy val c = 3 + + class Bar // not exported, but should not fail + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + + expect(foo.a).toEqual(1) + expect(foo.b).toEqual(2) + expect(foo.c).toEqual(3) + } + + it("should not export synthetic members with @JSExportAll - #1195") { + @JSExportAll + case class Foo(x: Int) + + val foo = Foo(1).asInstanceOf[js.Dynamic] + + expect(foo.x).toEqual(1) + expect(foo.copy).toBeUndefined + } + + it("should allow mutliple equivalent JSExport annotations") { + class Foo { + @JSExport + @JSExport("a") + @JSExport + @JSExport("a") + def b = 1 + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + + expect(foo.b).toEqual(1) + } + + it("should support named exports") { + import js.Dynamic.{literal => lit} + + class FooNamed { + @JSExportNamed("bar1") + def bar(x: Int, y: Int) = x + y + + @JSExportNamed("bar2") + @JSExport + def bar(x: Int = 1)(y: Int = x)(z: Int = y) = x + y + z + } + + val foo = (new FooNamed).asInstanceOf[js.Dynamic] + + expect(foo.bar1(lit(x = 1, y = 2))).toEqual(3) + if (JasmineTestFramework.hasTag("compliant-asinstanceof")) + expect(() => foo.bar1(lit(x = 1))).toThrow // missing arg + expect(foo.bar2(lit())).toEqual(3) + expect(foo.bar2(lit(x = 2))).toEqual(6) + expect(foo.bar2(lit(y = 2))).toEqual(5) + expect(foo.bar2(lit(y = 2, z = 1))).toEqual(4) + expect(foo.bar(2)).toEqual(6) + expect(foo.bar(2,3)).toEqual(8) + } + + it("should support named constructor exports") { + import js.Dynamic.{literal => lit} + + val constr = jsPackage.ExportedNamedArgClass + expect(js.Dynamic.newInstance(constr)(lit(x = 2)).result).toEqual("22true") + expect(js.Dynamic.newInstance(constr)(lit(y = "foo")).result).toEqual("1foofalse") + expect(js.Dynamic.newInstance(constr)(lit(z = true, y = "foo")).result).toEqual("1footrue") + } + + it("should support exporting under 'org' namespace - #364") { + val accessor = js.Dynamic.global.org.ExportedUnderOrgObject + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBe(ExportedUnderOrgObject.asInstanceOf[js.Any]) + } + + when("compliant-asinstanceof"). + it("should reject bad values for arguments of primitive value type") { + class Foo { + @JSExport + def doBool(x: Boolean) = x + @JSExport + def doChar(x: Char) = x + @JSExport + def doByte(x: Byte) = x + @JSExport + def doShort(x: Short) = x + @JSExport + def doInt(x: Int) = x + @JSExport + def doLong(x: Long) = x + @JSExport + def doFloat(x: Float) = x + @JSExport + def doDouble(x: Double) = x + @JSExport + def doUnit(x: Unit) = x + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + + // Nulls + expect(() => foo.doBool(null)).toThrow + expect(() => foo.doChar(null)).toThrow + expect(() => foo.doByte(null)).toThrow + expect(() => foo.doShort(null)).toThrow + expect(() => foo.doInt(null)).toThrow + expect(() => foo.doLong(null)).toThrow + expect(() => foo.doFloat(null)).toThrow + expect(() => foo.doDouble(null)).toThrow + expect(() => foo.doUnit(null)).toThrow + + // Class type + expect(() => foo.doBool(foo)).toThrow + expect(() => foo.doChar(foo)).toThrow + expect(() => foo.doByte(foo)).toThrow + expect(() => foo.doShort(foo)).toThrow + expect(() => foo.doInt(foo)).toThrow + expect(() => foo.doLong(foo)).toThrow + expect(() => foo.doFloat(foo)).toThrow + expect(() => foo.doDouble(foo)).toThrow + expect(() => foo.doUnit(foo)).toThrow + + // Bad values + expect(() => foo.doBool(1)).toThrow + expect(() => foo.doBool("a")).toThrow + + expect(() => foo.doChar(1)).toThrow + expect(() => foo.doChar("a")).toThrow + + expect(() => foo.doByte(300)).toThrow + expect(() => foo.doByte("a")).toThrow + + expect(() => foo.doShort(32768)).toThrow + expect(() => foo.doShort("a")).toThrow + + expect(() => foo.doInt(3.2)).toThrow + expect(() => foo.doInt("a")).toThrow + + expect(() => foo.doLong(3.2)).toThrow + expect(() => foo.doLong(3)).toThrow + expect(() => foo.doLong("a")).toThrow + + expect(() => foo.doFloat("a")).toThrow + } + + when("compliant-asinstanceof"). + it("should reject bad values for arguments of value class type - #613") { + class Foo { + @JSExport + def doVC(x: SomeValueClass) = x + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + + expect(() => foo.doVC(null)).toThrow + expect(() => foo.doVC(foo)).toThrow + expect(() => foo.doVC(1)).toThrow + expect(() => foo.doVC("a")).toThrow + } + + when("compliant-asinstanceof"). + it("should reject bad values for arguments of class type") { + class A + class B + + class Foo { + @JSExport + def doA(x: A) = x + } + + val foo = (new Foo).asInstanceOf[js.Dynamic] + + expect(() => foo.doA(1)).toThrow + expect(() => foo.doA((new B).asInstanceOf[js.Any])).toThrow + expect(() => foo.doA("a")).toThrow + } + + it("should offer exports for classes ending in _= - #1090") { + val constr = jsPackage.ExportClassSetterNamed_= + val obj = js.Dynamic.newInstance(constr)() + expect(obj.x).toBe(1) + } + + it("should offer exports for objects ending in _= - #1090") { + expect(jsPackage.ExportObjSetterNamed_=().x).toBe(1) + } + + } // describe + + describe("@JSExportDescendentObjects") { + + it("should offer auto exports for objects extending a trait") { + val accessor = + js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedTraitObject + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(obj).toBe(AutoExportedTraitObject.asInstanceOf[js.Any]) + } + + it("should offer auto exports for objects extending a class") { + val accessor = + js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedClassObject + expect(accessor).toBeDefined + expect(js.typeOf(accessor)).toEqual("function") + val obj = accessor() + expect(obj).toBeDefined + expect(obj).toBe(AutoExportedClassObject.asInstanceOf[js.Any]) + } + + } + + describe("@JSExportDescendentClasses") { + + it("should offer auto exports for classes extending a trait") { + val ctor = + js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedTraitClass + expect(ctor).toBeDefined + expect(js.typeOf(ctor)).toEqual("function") + + val obj1 = js.Dynamic.newInstance(ctor)() + expect(obj1).toBeDefined + expect(obj1.x).toBe(5) + + val obj2 = js.Dynamic.newInstance(ctor)(100) + expect(obj2).toBeDefined + expect(obj2.x).toBe(100) + } + + it("should offer auto exports for classes extending a class") { + val ctor = + js.Dynamic.global.scala.scalajs.testsuite.jsinterop.AutoExportedClassClass + expect(ctor).toBeDefined + expect(js.typeOf(ctor)).toEqual("function") + + val obj1 = js.Dynamic.newInstance(ctor)() + expect(obj1).toBeDefined + expect(obj1.x).toBe(5) + + val obj2 = js.Dynamic.newInstance(ctor)(100) + expect(obj2).toBeDefined + expect(obj2.x).toBe(100) + } + + } + +} + +object ExportNameHolder { + final val className = "ConstantFoldedClassExport" + final val objectName = "ConstantFoldedObjectExport" + final val methodName = "myMethod" +} + +@JSExport +@JSExport("TheExportedObject") +@JSExport("qualified.testobject.ExportedObject") // purposefully halfway the same as ExportedClass +@JSExport(ExportNameHolder.objectName) +object ExportedObject { + @JSExport + def witness: String = "witness" +} + +@JSExport +protected object ProtectedExportedObject { + @JSExport + def witness: String = "witness" +} + +@JSExport +@JSExport("TheExportedClass") +@JSExport("qualified.testclass.ExportedClass") // purposefully halfway the same as ExportedObject +@JSExport(ExportNameHolder.className) +class ExportedClass(_x: Int) { + @JSExport + val x = _x +} + +@JSExport +protected class ProtectedExportedClass(_x: Int) { + @JSExport + val x = _x +} + +@JSExport +class ExportedVarArgClass(x: String*) { + + @JSExport + def this(x: Int, y: String) = this(s"Number: <$x>", y) + + @JSExport + def result = x.mkString("|") +} + +@JSExport +class ExportedDefaultArgClass(x: Int, y: Int, z: Int) { + + @JSExport + def this(x: Int, y: Int = 5) = this(x, y, 100) + + @JSExport + def result = x + y + z +} + +@JSExport("org.ExportedUnderOrgObject") +object ExportedUnderOrgObject + +@JSExportDescendentClasses +@JSExportDescendentObjects +trait AutoExportTrait + +object AutoExportedTraitObject extends AutoExportTrait +class AutoExportedTraitClass(_x: Int) extends AutoExportTrait { + def this() = this(5) + @JSExport + def x: Int = _x +} + +@JSExportDescendentClasses +@JSExportDescendentObjects +class AutoExportClass + +object AutoExportedClassObject extends AutoExportClass +class AutoExportedClassClass(_x: Int) extends AutoExportTrait { + def this() = this(5) + @JSExport + def x: Int = _x +} + +class SomeValueClass(val i: Int) extends AnyVal + +@JSExportNamed +class ExportedNamedArgClass(x: Int = 1)(y: String = x.toString)(z: Boolean = y != "foo") { + @JSExport + val result = x + y + z +} + +@JSExport +class ExportClassSetterNamed_= { + @JSExport + val x = 1 +} + +@JSExport +object ExportObjSetterNamed_= { + @JSExport + val x = 1 +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala new file mode 100644 index 0000000..4973e64 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/FunctionTest.scala @@ -0,0 +1,41 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +object FunctionTest extends JasmineTest { + + import js.Dynamic.{literal => lit} + + describe("scala.scalajs.js.Function") { + + it("should support call() with expanded arguments") { + val f = js.eval(""" + var f = function() { return arguments; }; f; + """).asInstanceOf[js.Function] + + expect(f.call(null, 42, true)).toEqual(lit( + `0` = 42, + `1` = true)) + } + + it("should support call() with the :_* notation to expand a Seq") { + val f = js.eval(""" + var f = function() { return arguments; }; f; + """).asInstanceOf[js.Function] + + val args = Seq[js.Any](42, true) + expect(f.call(null, args: _*)).toEqual(lit( + `0` = 42, + `1` = true)) + } + + } +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala new file mode 100644 index 0000000..08211c3 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/MiscInteropTest.scala @@ -0,0 +1,89 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +object MiscInteropTest extends JasmineTest { + + describe("scala.scalajs.js.package") { + + it("should provide an equivalent to `typeof x`") { + import js.typeOf + expect(typeOf(5)).toEqual("number") + expect(typeOf(false)).toEqual("boolean") + expect(typeOf("hello")).toEqual("string") + expect(typeOf(null)).toEqual("object") + expect(typeOf(new js.Object)).toEqual("object") + expect(typeOf(())).toEqual("undefined") + expect(typeOf(() => 42)).toEqual("function") + } + } + + describe("scala.scalajs.js.Object") { + + it("should provide an equivalent to `p in o`") { + import js.Object.{ hasProperty => hasProp } + val o = js.Dynamic.literal(foo = 5, bar = "foobar").asInstanceOf[js.Object] + expect(hasProp(o, "foo")).toBeTruthy + expect(hasProp(o, "foobar")).toBeFalsy + expect(hasProp(o, "toString")).toBeTruthy // in prototype + } + + it("should respect evaluation order for `hasProperty`") { + import js.Object.{ hasProperty => hasProp } + var indicator = 3 + def o() = { + indicator += 4 + js.Dynamic.literal(x = 5).asInstanceOf[js.Object] + } + def p() = { + indicator *= 2 + "x" + } + expect(hasProp(o(), p())).toBeTruthy + expect(indicator).toEqual(14) + } + + it("should provide equivalent of JS for-in loop of {} - #13") { + val obj = js.eval("var dictionaryTest13 = { a: 'Scala.js', b: 7357 }; dictionaryTest13;") + val dict = obj.asInstanceOf[js.Dictionary[js.Any]] + var propCount = 0 + var propString = "" + + for (prop <- js.Object.properties(dict)) { + propCount += 1 + propString += dict(prop) + } + + expect(propCount).toEqual(2) + expect(propString).toEqual("Scala.js7357") + } + + it("should provide equivalent of JS for-in loop of [] - #13") { + val obj = js.eval("var arrayTest13 = [ 7, 3, 5, 7 ]; arrayTest13;") + val array = obj.asInstanceOf[js.Dictionary[js.Any]] + var propCount = 0 + var propString = "" + + for (prop <- js.Object.properties(array)) { + propCount += 1 + propString += array(prop) + } + + expect(propCount).toEqual(4) + expect(propString).toEqual("7357") + } + + it("should compile js.undefined") { + expect(() => js.undefined.asInstanceOf[js.prim.Number].toString(10)).toThrow + } + } + +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala new file mode 100644 index 0000000..589f379 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/RuntimeLongTest.scala @@ -0,0 +1,140 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.runtime.RuntimeLong + +import org.scalajs.jasmine.JasmineExpectation +import org.scalajs.jasminetest.JasmineTest + +import scala.util.Try + +/** + * test the runtime Long implementation directly + * does not depend on magic compiler Long rewriting + */ +object RuntimeLongTest extends JasmineTest { + + import RuntimeLong.fromDouble + + /** overload expect for long to add toString */ + def expect(l: RuntimeLong): JasmineExpectation = expect(l.toHexString) + + describe("scala.scalajs.runtime.RuntimeLong") { + + def fromInt(x: Int): RuntimeLong = new RuntimeLong(x) + + val maxInt = fromInt(Int.MaxValue) + val minInt = fromInt(Int.MinValue) + val one = fromInt(1) + val billion = fromInt(1000000000) + + val `4503599627370510L` = new RuntimeLong( 14, 0, 256) + val `613354684553L` = new RuntimeLong( 639113, 146235, 0) + val `9863155567412L` = new RuntimeLong(2247476, 2351559, 0) + val `3632147899696541255L` = new RuntimeLong(1568327, 2954580, 206463) + val `7632147899696541255L` = new RuntimeLong(2616903, 1593290, 433837) + + it("should correctly implement negation") { + expect(-fromInt(5)).toEqual("fffffffffffffffb") + expect(-fromInt(0)).toEqual("0") + expect(-minInt ).toEqual("80000000") + } + + it("should correctly implement comparison") { + expect(fromInt(7) < fromInt(15)).toBe(true) + expect(fromInt(15) < fromInt(15)).toBe(false) + expect(fromInt(15) <= fromInt(15)).toBe(true) + expect(fromInt(14) <= fromInt(15)).toBe(true) + expect(fromInt(15) > fromInt(15)).toBe(false) + expect(fromInt(14) > fromInt(15)).toBe(false) + expect(fromInt(16) > fromInt(15)).toBe(true) + expect(fromInt(15) >= fromInt(15)).toBe(true) + expect(fromInt(14) >= fromInt(15)).toBe(false) + expect(fromInt(16) >= fromInt(15)).toBe(true) + } + + it("should correctly implement addition") { + expect(fromInt(7) + fromInt(15)).toEqual("16") + expect( maxInt + maxInt ).toEqual("fffffffe") + expect( maxInt + one ).toEqual("80000000") + } + + it("should correctly implement subtraction") { + expect(fromInt(7) - fromInt(15)).toEqual("fffffffffffffff8") + expect( maxInt - maxInt ).toEqual("0") + } + + it("should correctly implement multiplication") { + expect(fromInt(7) * fromInt(15)).toEqual("69") + expect(fromInt(-7) * fromInt(15)).toEqual("ffffffffffffff97") + expect( maxInt * maxInt ).toEqual("3fffffff00000001") + expect(`4503599627370510L` * fromInt(-4)).toEqual("ffbfffffffffffc8") + } + + it("should correctly implement division") { + expect( fromInt(7) / fromInt(15)).toEqual("0") + expect( fromInt(24) / fromInt(5) ).toEqual("4") + expect( fromInt(24) / fromInt(-5)).toEqual("fffffffffffffffc") + expect( maxInt / fromInt(-5)).toEqual("ffffffffe6666667") + expect( maxInt / billion ).toEqual("2") + expect((maxInt+one) / billion ).toEqual("2") + } + + it("should correctly implement modulus") { + expect( fromInt(7) % fromInt(15)).toEqual("7") + expect( fromInt(24) % fromInt(5) ).toEqual("4") + expect( fromInt(24) % fromInt(-5)).toEqual("4") + expect( maxInt % billion ).toEqual("8ca6bff") + expect((maxInt+one) % billion ).toEqual("8ca6c00") + expect( maxInt % fromInt(-5)).toEqual("2") + } + + it("should correctly implement toString") { + expect(maxInt.toString).toEqual("2147483647") + expect(fromInt(-50).toString).toEqual("-50") + expect(fromInt(-1000000000).toString).toEqual("-1000000000") + expect((maxInt+one).toString).toEqual("2147483648") + expect(minInt.toString).toEqual("-2147483648") + } + + it("should correctly implement fromDouble") { + expect(fromDouble( 4.5)).toEqual("4") + expect(fromDouble(-4.5)).toEqual("fffffffffffffffc") + } + + it("should correctly implement toDouble") { + expect(fromInt(5).toDouble).toEqual(5.0) + expect((maxInt+one).toDouble).toEqual(2147483648.0) + } + + it("should correctly implement numberOfLeadingZeros") { + expect(fromInt( 0).numberOfLeadingZeros).toEqual(64) + expect(fromInt( 1).numberOfLeadingZeros).toEqual(63) + expect(fromInt(-1).numberOfLeadingZeros).toEqual(0) + expect(fromInt( 2).numberOfLeadingZeros).toEqual(62) + } + + it("should implement hashCode() according to spec in j.l.Long") { + expect(fromInt(0 ).hashCode()).toEqual(0) + expect(fromInt(55 ).hashCode()).toEqual(55) + expect(fromInt(-12 ).hashCode()).toEqual(11) + expect(fromInt(10006548).hashCode()).toEqual(10006548) + expect(fromInt(-1098748).hashCode()).toEqual(1098747) + + expect(`613354684553L` .hashCode()).toEqual(-825638905) + expect(`9863155567412L` .hashCode()).toEqual(1910653900) + expect(`3632147899696541255L`.hashCode()).toEqual(1735398658) + expect(`7632147899696541255L`.hashCode()).toEqual(-1689438124) + } + + } + +} + + diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala new file mode 100644 index 0000000..846c80b --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/StrangeNamedTests.scala @@ -0,0 +1,28 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import org.scalajs.jasminetest.JasmineTest + +object `1_TestName` extends JasmineTest { + describe("a test with name 1_TestName") { + it("should run") {} + } +} + +object eval extends JasmineTest { + describe("a test with name eval") { + it("should run") {} + } +} + +object `\u1f4a7` extends JasmineTest { + describe("a test with name \u1f4a9") { + it("should run") {} + } +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala new file mode 100644 index 0000000..4ac9058 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ThisFunctionTest.scala @@ -0,0 +1,70 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +object ThisFunctionTest extends JasmineTest { + + describe("scala.scalajs.js.ThisFunctionN") { + + it("should provide an implicit conversion from Scala function to js.ThisFunction") { + val g = js.eval(""" + var g = function(f, x) { return f.call(x, 42, x.foo); }; g; + """).asInstanceOf[js.Function2[js.ThisFunction2[ + js.Dynamic, Int, String, String], js.Dynamic, String]] + + val f = { (thiz: js.Dynamic, v: Int, u: String) => + expect(thiz).toBeTruthy() + expect(thiz.foobar).toEqual("foobar") + u + v + } + val obj = js.Object().asInstanceOf[js.Dynamic] + obj.foo = "foo" + obj.foobar = "foobar" + expect(g(f, obj)).toEqual("foo42") + } + + it("should accept a lambda where a js.ThisFunction is expected") { + val g = js.eval(""" + var g = function(f, x) { return f.call(x, 42, x.foo); }; g; + """).asInstanceOf[js.Function2[js.ThisFunction2[ + js.Dynamic, Int, String, String], js.Dynamic, String]] + + val obj = js.Object().asInstanceOf[js.Dynamic] + obj.foo = "foo" + obj.foobar = "foobar" + expect(g({ (thiz: js.Dynamic, v: Int, u: String) => + expect(thiz).toBeTruthy() + expect(thiz.foobar).toEqual("foobar") + u + v + }, obj)).toEqual("foo42") + } + + it("should bind the first argument to this when applying js.ThisFunctionN") { + val g = js.eval(""" + var g = function(x) { return this.foo + ":" + x; }; g; + """).asInstanceOf[js.ThisFunction1[js.Dynamic, Int, String]] + val obj = js.Object().asInstanceOf[js.Dynamic] + obj.foo = "foo" + expect(g(obj, 42)).toEqual("foo:42") + } + + it("should provide an implicit conversion from js.ThisFunction to Scala function") { + val g = js.eval(""" + var g = function(x) { return this.foo + ":" + x; }; g; + """).asInstanceOf[js.ThisFunction1[js.Dynamic, Int, String]] + val f: scala.Function2[js.Dynamic, Int, String] = g + val obj = js.Object().asInstanceOf[js.Dynamic] + obj.foo = "foo" + expect(f(obj, 42)).toEqual("foo:42") + } + + } +} diff --git a/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala new file mode 100644 index 0000000..28eae15 --- /dev/null +++ b/examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/UndefOrTest.scala @@ -0,0 +1,191 @@ +/* __ *\ +** ________ ___ / / ___ __ ____ Scala.js Test Suite ** +** / __/ __// _ | / / / _ | __ / // __/ (c) 2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-js.org/ ** +** /____/\___/_/ |_/____/_/ | |__/ /____/ ** +** |/____/ ** +\* */ +package scala.scalajs.testsuite.jsinterop + +import scala.scalajs.js +import org.scalajs.jasminetest.JasmineTest + +import js.annotation.JSExport + +object UndefOrTest extends JasmineTest { + + def some[A](v: A): js.UndefOr[A] = v + def none[A]: js.UndefOr[A] = js.undefined + + describe("scala.scalajs.js.UndefOr[A]") { + + it("convert A to js.UndefOr[A]") { + val x: js.UndefOr[Int] = 42 + expect(x.isEmpty).toBeFalsy + expect(x.isDefined).toBeTruthy + expect(x.nonEmpty).toBeTruthy + expect(x.get).toEqual(42) + } + + it("convert undefined to js.UndefOr[A]") { + val x: js.UndefOr[Int] = js.undefined + expect(x.isEmpty).toBeTruthy + expect(x.isDefined).toBeFalsy + expect(x.nonEmpty).toBeFalsy + expect(() => x.get).toThrow + } + + it("convert to js.Any when A <% js.Any") { + val x: js.UndefOr[Int] = 42 + expect(x).toEqual(42) + + val y: js.UndefOr[String] = js.undefined + expect(y).toBeUndefined + } + + it("getOrElse") { + expect(some("hello").getOrElse("ko")).toEqual("hello") + expect(none[String].getOrElse("ok")).toEqual("ok") + + var defaultComputed = false + expect(some("test") getOrElse { + defaultComputed = true + "ko" + }).toEqual("test") + expect(defaultComputed).toBeFalsy + } + + it("orNull") { + expect(some("hello").orNull).toEqual("hello") + expect(none[String].orNull).toBeNull + } + + it("map") { + expect(some(62).map(_ / 3)).toEqual(62 / 3) + expect(none[Int].map(_ / 3)).toBeUndefined + } + + it("fold") { + expect(some(3).fold(10)(_ * 2)).toEqual(6) + expect(none[Int].fold(10)(_ * 2)).toEqual(10) + } + + it("flatMap") { + def f(x: Int): js.UndefOr[Int] = if (x > 0) x+3 else js.undefined + expect(some(6).flatMap(f)).toEqual(9) + expect(some(-6).flatMap(f)).toBeUndefined + expect(none[Int].flatMap(f)).toBeUndefined + } + + it("flatten") { + expect(some(some(7)).flatten.isDefined).toBeTruthy + expect(some(some(7)).flatten.get).toEqual(7) + expect(some(none[Int]).flatten.isDefined).toBeFalsy + expect(none[js.UndefOr[Int]].flatten.isDefined).toBeFalsy + } + + it("filter") { + expect(some(7).filter(_ > 0).isDefined).toBeTruthy + expect(some(7).filter(_ > 0).get).toEqual(7) + expect(some(7).filter(_ < 0).isDefined).toBeFalsy + expect(none[Int].filter(_ < 0).isDefined).toBeFalsy + } + + it("filterNot") { + expect(some(7).filterNot(_ < 0).isDefined).toBeTruthy + expect(some(7).filterNot(_ < 0).get).toEqual(7) + expect(some(7).filterNot(_ > 0).isDefined).toBeFalsy + expect(none[Int].filterNot(_ > 0).isDefined).toBeFalsy + } + + it("exists") { + expect(some(7).exists(_ > 0)).toBeTruthy + expect(some(7).exists(_ < 0)).toBeFalsy + expect(none[Int].exists(_ > 0)).toBeFalsy + } + + it("forall") { + expect(some(7).forall(_ > 0)).toBeTruthy + expect(some(7).forall(_ < 0)).toBeFalsy + expect(none[Int].forall(_ > 0)).toBeTruthy + } + + it("foreach") { + var witness1 = 3 + some(42).foreach(witness1 = _) + expect(witness1).toEqual(42) + + var witness2 = 3 + none[Int].foreach(witness2 = _) + expect(witness2).toEqual(3) + } + + it("collect") { + expect(some("hello") collect { + case "hello" => "ok" + }).toEqual("ok") + expect(some("hello") collect { + case "notthis" => "ko" + }).toBeUndefined + expect(none[String] collect { + case "hello" => "ko" + }).toBeUndefined + } + + it("collect should call guard at most once") { + var witness = 0 + def guard(x: String) = { + witness += 1 + true + } + expect(some("hello") collect { + case x @ "hello" if guard(x) => "ok" + }).toEqual("ok") + expect(witness).toEqual(1) + } + + it("orElse") { + expect(some(true) orElse some(false)).toBeTruthy + expect(some("ok") orElse none).toEqual("ok") + expect(none orElse some("yes")).toEqual("yes") + expect(none orElse none).toBeUndefined + } + + it("toList") { + import scala.scalajs.js.JSConverters._ + + expect(some("hello").toList.toJSArray).toEqual(js.Array("hello")) + expect(none[String].toList.toJSArray).toEqual(js.Array()) + } + + it("toLeft and toRight") { + expect(some("left").toLeft("right").isInstanceOf[Left[_, _]]).toBeTruthy + expect(none[String].toLeft("right").isInstanceOf[Right[_, _]]).toBeTruthy + expect(some("right").toRight("left").isInstanceOf[Right[_, _]]).toBeTruthy + expect(none[String].toRight("left").isInstanceOf[Left[_, _]]).toBeTruthy + } + + it("toOption") { + expect(some("foo").toOption == Some("foo")).toBeTruthy + expect(none.toOption == None).toBeTruthy + } + + } + + describe("scala.scalajs.js.JSConverters.JSRichOption") { + + import js.JSConverters._ + + it("should provide orUndefined") { + expect(Some("asdf").orUndefined).toEqual("asdf") + expect((None: Option[String]).orUndefined).toBeUndefined + + // This doesn't work on 2.10, since it doesn't infer + // Nothing <:< js.Any to implicitly convert UndefOr[Nothing] to + // js.Any + // expect(None.orUndefined).toBeUndefined + } + + } + +} |