diff options
Diffstat (limited to 'examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala')
-rw-r--r-- | examples/scala-js/test-suite/src/test/scala/scala/scalajs/testsuite/jsinterop/ExportsTest.scala | 1075 |
1 files changed, 1075 insertions, 0 deletions
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 +} |