diff options
Diffstat (limited to 'tests/js')
41 files changed, 747 insertions, 407 deletions
diff --git a/tests/js/readme.md b/tests/js/readme.md new file mode 100644 index 000000000..7fcc722fa --- /dev/null +++ b/tests/js/readme.md @@ -0,0 +1,5 @@ +## notes +Prefer moving tests to a non-js directory so that they get tested across all backends automatically. +Ideally, tests/js should be reserved to code that only makes sense in js. + +Note also that tests for a js specific module (e.g.: `std/jsbigints`) belong to `tests/stdlib`, (e.g.: `tests/stdlib/tjsbigints.nim`) diff --git a/tests/js/t11166.nim b/tests/js/t11166.nim index 8dd77925c..e98ccda10 100644 --- a/tests/js/t11166.nim +++ b/tests/js/t11166.nim @@ -1,4 +1,20 @@ +discard """ + output: ''' +test1 +test2 +''' +""" + import jsffi +type + C = object + props: int + +var c: C + when compiles(c.props): - echo "test" + echo "test1" + +when not compiles(d.props): + echo "test2" diff --git a/tests/js/t17177.nim b/tests/js/t17177.nim new file mode 100644 index 000000000..fc362cec1 --- /dev/null +++ b/tests/js/t17177.nim @@ -0,0 +1,10 @@ +import std/asyncjs + +proc fn1(n: int): Future[int] {.async.} = return n +proc main2() = + proc fn2(n: int): Future[int] {.async.} = return n +proc main3(a: auto) = + proc fn3(n: int): Future[int] {.async.} = return n +proc main4() {.async.} = + proc fn4(n: int): Future[int] {.async.} = return n + discard diff --git a/tests/js/t20233.nim b/tests/js/t20233.nim new file mode 100644 index 000000000..401d14122 --- /dev/null +++ b/tests/js/t20233.nim @@ -0,0 +1,7 @@ +discard """ + output: "yes" +""" +case 1.0 +of 1.0..2.0, 4.0: echo "yes" +of 3.0: discard +else: echo "no" \ No newline at end of file diff --git a/tests/js/t20235.nim b/tests/js/t20235.nim new file mode 100644 index 000000000..3a69c2bd6 --- /dev/null +++ b/tests/js/t20235.nim @@ -0,0 +1,11 @@ +discard """ + action: "run" + output: "0 4" +""" + +proc main = + var s = "" + s.setLen(4) + echo s[0].ord, " ", s.len + +main() diff --git a/tests/js/t21209.nim b/tests/js/t21209.nim new file mode 100644 index 000000000..4de34f035 --- /dev/null +++ b/tests/js/t21209.nim @@ -0,0 +1,6 @@ +discard """ + action: "compile" + cmd: "nim check --warning[UnusedImport]:off $file" +""" + +import std/times diff --git a/tests/js/t21209.nims b/tests/js/t21209.nims new file mode 100644 index 000000000..318e28f97 --- /dev/null +++ b/tests/js/t21209.nims @@ -0,0 +1 @@ +--b:js \ No newline at end of file diff --git a/tests/js/t21247.nim b/tests/js/t21247.nim new file mode 100644 index 000000000..5b38787b3 --- /dev/null +++ b/tests/js/t21247.nim @@ -0,0 +1,15 @@ +import std/typetraits + +type + QueryParams* = distinct seq[(string, string)] + +converter toBase*(params: var QueryParams): var seq[(string, string)] = + params.distinctBase + +proc foo(): QueryParams = + # Issue was that the implicit converter call didn't say that it took the + # address of the parameter it was converting. This led to the parameter not being + # passed as a fat pointer which toBase expected + result.add(("hello", "world")) + +assert foo().distinctBase() == @[("hello", "world")] diff --git a/tests/js/t21439.nim b/tests/js/t21439.nim new file mode 100644 index 000000000..3caeb090a --- /dev/null +++ b/tests/js/t21439.nim @@ -0,0 +1,11 @@ +proc test(a: openArray[string]): proc = + let a = @a + result = proc = + for i in a: + discard i + + +const a = ["t1", "t2"] + +discard test(a) + diff --git a/tests/js/t7109.nim b/tests/js/t7109.nim index 0d071dbbf..a1a3b718e 100644 --- a/tests/js/t7109.nim +++ b/tests/js/t7109.nim @@ -1,8 +1,8 @@ -discard """ - errormsg: "Closure iterators are not supported by JS backend!" -""" - iterator iter*(): int {.closure.} = yield 3 var x = iter +doAssert x() == 3 + +let fIt = iterator(): int = yield 70 +doAssert fIt() == 70 diff --git a/tests/js/t8821.nim b/tests/js/t8821.nim index 43cf3f6f2..38c88efa8 100644 --- a/tests/js/t8821.nim +++ b/tests/js/t8821.nim @@ -1,6 +1,3 @@ -discard """ - errormsg: "Your case statement contains too many branches, consider using if/else instead!" -""" proc isInt32(i: int): bool = case i @@ -9,4 +6,4 @@ proc isInt32(i: int): bool = else: return false -discard isInt32(1) \ No newline at end of file +doAssert isInt32(1) == true \ No newline at end of file diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim index 78c329a24..042520dc5 100644 --- a/tests/js/t9410.nim +++ b/tests/js/t9410.nim @@ -447,10 +447,10 @@ template tests = doAssert seqOfSeqs == @[@[10, 2], @[30, 4]] when false: - block: # openarray + block: # openArray # Error: internal error: genAddr: nkStmtListExpr var calls = 0 - proc getvarint(x: var openarray[int]): var int = + proc getvarint(x: var openArray[int]): var int = calls += 1 if true: x[1] diff --git a/tests/js/tasync.nim b/tests/js/tasync.nim deleted file mode 100644 index 318237651..000000000 --- a/tests/js/tasync.nim +++ /dev/null @@ -1,33 +0,0 @@ -discard """ - output: ''' -x -e -''' -""" - -import asyncjs - -# demonstrate forward definition -# for js -proc y(e: int): Future[string] {.async.} - -proc e: int {.discardable.} = - echo "e" - return 2 - -proc x(e: int): Future[void] {.async.} = - var s = await y(e) - if e > 2: - return - echo s - e() - -proc y(e: int): Future[string] {.async.} = - if e > 0: - return await y(0) - else: - return "x" - - -discard x(2) - diff --git a/tests/js/tasyncjs.nim b/tests/js/tasyncjs.nim new file mode 100644 index 000000000..f3b273c44 --- /dev/null +++ b/tests/js/tasyncjs.nim @@ -0,0 +1,107 @@ +discard """ + output: ''' +x +e +done +''' +""" + +#[ +xxx move this to tests/stdlib/tasyncjs.nim +]# + +import std/asyncjs + +block: + # demonstrate forward definition for js + proc y(e: int): Future[string] {.async.} + + proc e: int {.discardable.} = + echo "e" + return 2 + + proc x(e: int): Future[void] {.async.} = + var s = await y(e) + if e > 2: + return + echo s + e() + + proc y(e: int): Future[string] {.async.} = + if e > 0: + return await y(0) + else: + return "x" + + discard x(2) + +import std/sugar +from std/strutils import contains + +var witness: seq[string] + +proc fn(n: int): Future[int] {.async.} = + if n >= 7: + raise newException(ValueError, "foobar: " & $n) + if n > 0: + var ret = 1 + await fn(n-1) + witness.add $(n, ret) + return ret + else: + return 10 + +proc asyncFact(n: int): Future[int] {.async.} = + if n > 0: result = n * await asyncFact(n-1) + else: result = 1 + +proc asyncIdentity(n: int): Future[int] {.async.} = + if n > 0: result = 1 + await asyncIdentity(n-1) + else: result = 0 + +proc main() {.async.} = + block: # then + let x = await fn(4) + .then((a: int) => a.float) + .then((a: float) => $a) + doAssert x == "14.0" + doAssert witness == @["(1, 11)", "(2, 12)", "(3, 13)", "(4, 14)"] + + doAssert (await fn(2)) == 12 + + let x2 = await fn(4).then((a: int) => (discard)).then(() => 13) + doAssert x2 == 13 + + let x4 = await asyncFact(3).then(asyncIdentity).then(asyncIdentity).then((a:int) => a * 7).then(asyncIdentity) + doAssert x4 == 3 * 2 * 7 + + block: # bug #17177 + proc asyncIdentityNested(n: int): Future[int] {.async.} = return n + let x5 = await asyncFact(3).then(asyncIdentityNested) + doAssert x5 == 3 * 2 + + when false: # xxx pending bug #17254 + let x6 = await asyncFact(3).then((a:int) {.async.} => a * 11) + doAssert x6 == 3 * 2 * 11 + + block: # catch + var reason: Error + await fn(6).then((a: int) => (witness.add $a)).catch((r: Error) => (reason = r)) + doAssert reason == nil + + await fn(7).then((a: int) => (discard)).catch((r: Error) => (reason = r)) + doAssert reason != nil + doAssert reason.name == "Error" + doAssert "foobar: 7" in $reason.message + echo "done" # justified here to make sure we're running this, since it's inside `async` + +block asyncPragmaInType: + type Handler = proc () {.async.} + proc foo() {.async.} = discard + var x: Handler = foo + +block: # 13341 + proc f {.async.} = + proc g: int = + result = 123 + +discard main() diff --git a/tests/js/tasyncjs_bad.nim b/tests/js/tasyncjs_bad.nim new file mode 100644 index 000000000..b1e5a7bc3 --- /dev/null +++ b/tests/js/tasyncjs_bad.nim @@ -0,0 +1,22 @@ +discard """ + exitCode: 1 + outputsub: "Error: unhandled exception: foobar: 13" +""" + +# note: this needs `--unhandled-rejections=strict`, see D20210217T215950 + +import std/asyncjs +from std/sugar import `=>` + +proc fn(n: int): Future[int] {.async.} = + if n >= 7: raise newException(ValueError, "foobar: " & $n) + else: result = n + +proc main() {.async.} = + let x1 = await fn(6) + doAssert x1 == 6 + await fn(7).catch((a: Error) => (discard)) + let x3 = await fn(13) + doAssert false # shouldn't go here, should fail before + +discard main() diff --git a/tests/js/tasync_pragma.nim b/tests/js/tasyncjs_pragma.nim index 916769fad..2b6f32e92 100644 --- a/tests/js/tasync_pragma.nim +++ b/tests/js/tasyncjs_pragma.nim @@ -5,6 +5,8 @@ t ''' """ +# xxx merge into tasyncjs.nim + import asyncjs, macros macro f*(a: untyped): untyped = diff --git a/tests/js/tbasics.nim b/tests/js/tbasics.nim index b297bb037..ef84f8bb5 100644 --- a/tests/js/tbasics.nim +++ b/tests/js/tbasics.nim @@ -54,7 +54,7 @@ for x in someGlobal: doAssert(x == 0) proc tdefault = var x = default(int) doAssert(x == 0) - proc inner(v: openarray[string]) = + proc inner(v: openArray[string]) = doAssert(v.len == 0) inner(default(seq[string])) diff --git a/tests/js/tbigint_backend.nim b/tests/js/tbigint_backend.nim new file mode 100644 index 000000000..8f2f6c4f4 --- /dev/null +++ b/tests/js/tbigint_backend.nim @@ -0,0 +1,57 @@ +import std/private/jsutils + + +type JsBigIntImpl {.importc: "bigint".} = int +type JsBigInt = distinct JsBigIntImpl + +doAssert JsBigInt isnot int +func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} +func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} +func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} +func `==`*(x, y: JsBigInt): bool {.importjs: "(# === #)".} +func inc*(x: var JsBigInt) {.importjs: "[#][0][0]++".} +func inc2*(x: var JsBigInt) {.importjs: "#++".} +func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".} +func `$`*(this: JsBigInt): string = + $toCstring(this) + +block: + doAssert defined(nimHasJsBigIntBackend) + let z1 = big"10" + let z2 = big"15" + doAssert z1 == big"10" + doAssert z1 == z1 + doAssert z1 != z2 + var s: seq[cstring] + for i in z1 .. z2: + s.add $i + doAssert s == @["10".cstring, "11", "12", "13", "14", "15"] + block: + var a=big"3" + a.inc + doAssert a == big"4" + block: + var z: JsBigInt + doAssert $z == "0" + doAssert z.jsTypeOf == "bigint" # would fail without codegen change + doAssert z != big(1) + doAssert z == big"0" # ditto + + # ditto below + block: + let z: JsBigInt = big"1" + doAssert $z == "1" + doAssert z.jsTypeOf == "bigint" + doAssert z == big"1" + + block: + let z = JsBigInt.default + doAssert $z == "0" + doAssert z.jsTypeOf == "bigint" + doAssert z == big"0" + + block: + var a: seq[JsBigInt] + a.setLen 3 + doAssert a[^1].jsTypeOf == "bigint" + doAssert a[^1] == big"0" diff --git a/tests/js/tbyvar.nim b/tests/js/tbyvar.nim index a4c60b0b3..93724a2f1 100644 --- a/tests/js/tbyvar.nim +++ b/tests/js/tbyvar.nim @@ -39,9 +39,9 @@ proc main = main() -# Test: pass var seq to var openarray +# Test: pass var seq to var openArray var s = @[2, 1] -proc foo(a: var openarray[int]) = a[0] = 123 +proc foo(a: var openArray[int]) = a[0] = 123 proc bar(s: var seq[int], a: int) = doAssert(a == 5) diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim index 3137123bf..4f1c28de3 100644 --- a/tests/js/tclosures.nim +++ b/tests/js/tclosures.nim @@ -22,7 +22,7 @@ asm """ function print (text) { console.log (text); } """ -proc consoleprint (str:cstring): void {.importc: "print", noDecl.} +proc consoleprint (str:cstring): void {.importc: "print", nodecl.} proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")] type CallbackProc {.importc.} = proc () : cstring diff --git a/tests/js/tcodegendeclproc.nim b/tests/js/tcodegendeclproc.nim index 3acf0bc13..33064bdf1 100644 --- a/tests/js/tcodegendeclproc.nim +++ b/tests/js/tcodegendeclproc.nim @@ -3,7 +3,7 @@ discard """ -1 8 ''' - ccodecheck: "'console.log(-1); function fac_' \\d+ '(n_' \\d+ ')'" + ccodecheck: "'console.log(-1); function fac__tcodegendeclproc_u1(n_p0)'" """ proc fac(n: int): int {.codegenDecl: "console.log(-1); function $2($3)".} = return n diff --git a/tests/js/tcsymbol.nim b/tests/js/tcsymbol.nim new file mode 100644 index 000000000..07e52b9b6 --- /dev/null +++ b/tests/js/tcsymbol.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--cc:gcc; --cc:tcc" +""" + +doAssert not defined(gcc) +doAssert not defined(tcc) \ No newline at end of file diff --git a/tests/js/tdanger.nim b/tests/js/tdanger.nim new file mode 100644 index 000000000..9088859a8 --- /dev/null +++ b/tests/js/tdanger.nim @@ -0,0 +1,17 @@ +discard """ + matrix: ";--d:danger" +""" + +block: + proc foo() = + var name = int64(12) + var x = uint32(name) + var m = x + 12 + + var y = int32(name) + var n = y + 1 + + doAssert m == uint32(n + 11) + + + foo() diff --git a/tests/js/tfieldchecks.nim b/tests/js/tfieldchecks.nim index 3916cae74..a0679a349 100644 --- a/tests/js/tfieldchecks.nim +++ b/tests/js/tfieldchecks.nim @@ -30,7 +30,7 @@ block: block: let a0 = addr(obj.f0) echo a0[] - # let a1 = unsafeAddr(obj.f1) + # let a1 = addr(obj.f1) # echo a1[] doAssertRaises(FieldDefect): let a2 = addr(obj.f2) diff --git a/tests/js/tindexdefect.nim b/tests/js/tindexdefect.nim new file mode 100644 index 000000000..37994ec2e --- /dev/null +++ b/tests/js/tindexdefect.nim @@ -0,0 +1,9 @@ +discard """ + outputsub: "unhandled exception: index 10000 not in 0 .. 0 [IndexDefect]" + exitcode: 1 + joinable: false +""" + +var s = ['a'] +let z = s[10000] == 'a' +echo z \ No newline at end of file diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index 143ac4a25..f27ea5546 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -1,291 +1,197 @@ discard """ +matrix: "--legacy:jsnolambdalifting;" output: ''' -true -true -true -true -true -true -true -true -true -true -true -true -true -true -true -true 3 2 12 Event { name: 'click: test' } Event { name: 'reloaded: test' } Event { name: 'updates: test' } -true -true -true -true -true -true -true ''' """ import jsffi, jsconsole # Tests for JsObject -# Test JsObject []= and [] -block: - proc test(): bool = - let obj = newJsObject() - var working = true - obj["a"] = 11 - obj["b"] = "test" - obj["c"] = "test".cstring - working = working and obj["a"].to(int) == 11 - working = working and obj["c"].to(cstring) == "test".cstring - working - echo test() - -# Test JsObject .= and . -block: - proc test(): bool = - let obj = newJsObject() - var working = true - obj.a = 11 - obj.b = "test" - obj.c = "test".cstring - obj.`$!&` = 42 - obj.`while` = 99 - working = working and obj.a.to(int) == 11 - working = working and obj.b.to(string) == "test" - working = working and obj.c.to(cstring) == "test".cstring - working = working and obj.`$!&`.to(int) == 42 - working = working and obj.`while`.to(int) == 99 - working - echo test() - -# Test JsObject .() -block: - proc test(): bool = - let obj = newJsObject() - obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z) - obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6" - echo test() - -# Test JsObject []() -block: - proc test(): bool = - let obj = newJsObject() - obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z) - let call = obj["a"].to(proc(x, y, z: int, t: string): string) - call(1, 2, 3, "Result is: ") == "Result is: 6" - echo test() +block: # Test JsObject []= and [] + let obj = newJsObject() + obj["a"] = 11 + obj["b"] = "test" + obj["c"] = "test".cstring + doAssert obj["a"].to(int) == 11 + doAssert obj["c"].to(cstring) == "test".cstring + +block: # Test JsObject .= and . + let obj = newJsObject() + obj.a = 11 + obj.b = "test" + obj.c = "test".cstring + obj.`$!&` = 42 + obj.`while` = 99 + doAssert obj.a.to(int) == 11 + doAssert obj.b.to(string) == "test" + doAssert obj.c.to(cstring) == "test".cstring + doAssert obj.`$!&`.to(int) == 42 + doAssert obj.`while`.to(int) == 99 + +block: # Test JsObject .() + let obj = newJsObject() + obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z) + doAssert obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == cstring"Result is: 6" + +block: # Test JsObject []() + let obj = newJsObject() + obj.a = proc(x, y, z: int, t: string): string = t & $(x + y + z) + let call = obj["a"].to(proc(x, y, z: int, t: string): string) + doAssert call(1, 2, 3, "Result is: ") == "Result is: 6" # Test JsObject Iterators -block: - proc testPairs(): bool = - let obj = newJsObject() - var working = true - obj.a = 10 - obj.b = 20 - obj.c = 30 - for k, v in obj.pairs: - case $k - of "a": - working = working and v.to(int) == 10 - of "b": - working = working and v.to(int) == 20 - of "c": - working = working and v.to(int) == 30 - else: - return false - working - proc testItems(): bool = - let obj = newJsObject() - var working = true - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.items: - working = working and v.to(int) in [10, 20, 30] - working - proc testKeys(): bool = - let obj = newJsObject() - var working = true - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.keys: - working = working and $v in ["a", "b", "c"] - working - proc test(): bool = testPairs() and testItems() and testKeys() - echo test() - -# Test JsObject equality -block: - proc test(): bool = - {. emit: "var comparison = {a: 22, b: 'test'};" .} - var comparison {. importjs, nodecl .}: JsObject - let obj = newJsObject() - obj.a = 22 - obj.b = "test".cstring - obj.a == comparison.a and obj.b == comparison.b - echo test() - -# Test JsObject literal -block: - proc test(): bool = - {. emit: "var comparison = {a: 22, b: 'test'};" .} - var comparison {. importjs, nodecl .}: JsObject - let obj = JsObject{ a: 22, b: "test".cstring } - obj.a == comparison.a and obj.b == comparison.b - echo test() +block: # testPairs + let obj = newJsObject() + obj.a = 10 + obj.b = 20 + obj.c = 30 + for k, v in obj.pairs: + case $k + of "a": + doAssert v.to(int) == 10 + of "b": + doAssert v.to(int) == 20 + of "c": + doAssert v.to(int) == 30 + else: + doAssert false +block: # testItems + let obj = newJsObject() + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.items: + doAssert v.to(int) in [10, 20, 30] +block: # testKeys + let obj = newJsObject() + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.keys: + doAssert $v in ["a", "b", "c"] + +block: # Test JsObject equality + {. emit: "var comparison = {a: 22, b: 'test'};" .} + var comparison {. importjs, nodecl .}: JsObject + let obj = newJsObject() + obj.a = 22 + obj.b = "test".cstring + doAssert obj.a == comparison.a and obj.b == comparison.b + +block: # Test JsObject literal + {. emit: "var comparison = {a: 22, b: 'test'};" .} + var comparison {. importjs, nodecl .}: JsObject + let obj = JsObject{ a: 22, b: "test".cstring } + doAssert obj.a == comparison.a and obj.b == comparison.b # Tests for JsAssoc -# Test JsAssoc []= and [] -block: - proc test(): bool = - let obj = newJsAssoc[int, int]() - var working = true - obj[1] = 11 - working = working and not compiles(obj["a"] = 11) - working = working and not compiles(obj["a"]) - working = working and not compiles(obj[2] = "test") - working = working and not compiles(obj[3] = "test".cstring) - working = working and obj[1] == 11 - working - echo test() - -# Test JsAssoc .= and . -block: - proc test(): bool = - let obj = newJsAssoc[cstring, int]() - var working = true - obj.a = 11 - obj.`$!&` = 42 - working = working and not compiles(obj.b = "test") - working = working and not compiles(obj.c = "test".cstring) - working = working and obj.a == 11 - working = working and obj.`$!&` == 42 - working - echo test() - -# Test JsAssoc .() -block: - proc test(): bool = - let obj = newJsAssoc[cstring, proc(e: int): int]() - obj.a = proc(e: int): int = e * e - obj.a(10) == 100 - echo test() - -# Test JsAssoc []() -block: - proc test(): bool = - let obj = newJsAssoc[cstring, proc(e: int): int]() - obj.a = proc(e: int): int = e * e - let call = obj["a"] - call(10) == 100 - echo test() +block: # Test JsAssoc []= and [] + let obj = newJsAssoc[int, int]() + obj[1] = 11 + doAssert not compiles(obj["a"] = 11) + doAssert not compiles(obj["a"]) + doAssert not compiles(obj[2] = "test") + doAssert not compiles(obj[3] = "test".cstring) + doAssert obj[1] == 11 + +block: # Test JsAssoc .= and . + let obj = newJsAssoc[cstring, int]() + var working = true + obj.a = 11 + obj.`$!&` = 42 + doAssert not compiles(obj.b = "test") + doAssert not compiles(obj.c = "test".cstring) + doAssert obj.a == 11 + doAssert obj.`$!&` == 42 + +block: # Test JsAssoc .() + let obj = newJsAssoc[cstring, proc(e: int): int]() + obj.a = proc(e: int): int = e * e + doAssert obj.a(10) == 100 + +block: # Test JsAssoc []() + let obj = newJsAssoc[cstring, proc(e: int): int]() + obj.a = proc(e: int): int = e * e + let call = obj["a"] + doAssert call(10) == 100 # Test JsAssoc Iterators -block: - proc testPairs(): bool = - let obj = newJsAssoc[cstring, int]() - var working = true - obj.a = 10 - obj.b = 20 - obj.c = 30 - for k, v in obj.pairs: - case $k - of "a": - working = working and v == 10 - of "b": - working = working and v == 20 - of "c": - working = working and v == 30 - else: - return false - working - proc testItems(): bool = - let obj = newJsAssoc[cstring, int]() - var working = true - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.items: - working = working and v in [10, 20, 30] - working - proc testKeys(): bool = - let obj = newJsAssoc[cstring, int]() - var working = true - obj.a = 10 - obj.b = 20 - obj.c = 30 - for v in obj.keys: - working = working and v in [cstring"a", cstring"b", cstring"c"] - working - proc test(): bool = testPairs() and testItems() and testKeys() - echo test() - -# Test JsAssoc equality -block: - proc test(): bool = - {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] - let obj = newJsAssoc[cstring, int]() - obj.a = 22 - obj.b = 55 - obj.a == comparison.a and obj.b == comparison.b - echo test() - -# Test JsAssoc literal -block: - proc test(): bool = - {. emit: "var comparison = {a: 22, b: 55};" .} - var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] - let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } - var working = true - working = working and - compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) - working = working and - comparison.a == obj.a and comparison.b == obj.b - working = working and - not compiles(JsAssoc[cstring, int]{ a: "test" }) - working - echo test() +block: # testPairs + let obj = newJsAssoc[cstring, int]() + obj.a = 10 + obj.b = 20 + obj.c = 30 + for k, v in obj.pairs: + case $k + of "a": + doAssert v == 10 + of "b": + doAssert v == 20 + of "c": + doAssert v == 30 + else: + doAssert false +block: # testItems + let obj = newJsAssoc[cstring, int]() + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.items: + doAssert v in [10, 20, 30] +block: # testKeys + let obj = newJsAssoc[cstring, int]() + obj.a = 10 + obj.b = 20 + obj.c = 30 + for v in obj.keys: + doAssert v in [cstring"a", cstring"b", cstring"c"] + +block: # Test JsAssoc equality + {. emit: "var comparison = {a: 22, b: 55};" .} + var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] + let obj = newJsAssoc[cstring, int]() + obj.a = 22 + obj.b = 55 + doAssert obj.a == comparison.a and obj.b == comparison.b + +block: # Test JsAssoc literal + {. emit: "var comparison = {a: 22, b: 55};" .} + var comparison {. importjs, nodecl .}: JsAssoc[cstring, int] + let obj = JsAssoc[cstring, int]{ a: 22, b: 55 } + doAssert compiles(JsAssoc[int, int]{ 1: 22, 2: 55 }) + doAssert comparison.a == obj.a and comparison.b == obj.b + doAssert not compiles(JsAssoc[cstring, int]{ a: "test" }) # Tests for macros on non-JsRoot objects -# Test lit -block: +block: # Test lit type TestObject = object a: int b: cstring - proc test(): bool = - {. emit: "var comparison = {a: 1};" .} - var comparison {. importjs, nodecl .}: TestObject - let obj = TestObject{ a: 1 } - obj == comparison - echo test() + {. emit: "var comparison = {a: 1};" .} + var comparison {. importjs, nodecl .}: TestObject + let obj = TestObject{ a: 1 } + doAssert obj == comparison -# Test bindMethod -block: +block: # Test bindMethod type TestObject = object a: int - onWhatever: proc(e: int): int + onWhatever: proc(e: int): int {.nimcall.} proc handleWhatever(this: TestObject, e: int): int = e + this.a - proc test(): bool = + block: let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever)) - obj.onWhatever(1) == 10 - echo test() + doAssert obj.onWhatever(1) == 10 block: {.emit: "function jsProc(n) { return n; }" .} proc jsProc(x: int32): JsObject {.importjs: "jsProc(#)".} - - proc test() = + block: var x = jsProc(1) var y = jsProc(2) console.log x + y @@ -294,8 +200,6 @@ block: x += jsProc(10) console.log x - test() - block: {.emit: """ @@ -314,7 +218,7 @@ block: on("click") do (e: Event): console.log e - jslib.on("reloaded") do: + jslib.on("reloaded") do (): console.log jsarguments[0] # this test case is different from the above, because @@ -323,13 +227,13 @@ block: console.log jsarguments[0] block: - echo jsUndefined == jsNull - echo jsUndefined == nil - echo jsNull == nil - echo jsUndefined.isNil - echo jsNull.isNil - echo jsNull.isNull - echo jsUndefined.isUndefined + doAssert jsUndefined == jsNull + doAssert jsUndefined == nil + doAssert jsNull == nil + doAssert jsUndefined.isNil + doAssert jsNull.isNil + doAssert jsNull.isNull + doAssert jsUndefined.isUndefined block: # test ** var a = toJs(0) @@ -363,3 +267,8 @@ block: # test ** doAssert to(`**`(toJs(1) + toJs(1), toJs(2)), int) == 4 +block: # issue #21208 + type MyEnum = enum baz + var obj: JsObject + {.emit: "`obj` = {bar: {baz: 123}};".} + discard obj.bar.baz diff --git a/tests/js/tjsffi_old.nim b/tests/js/tjsffi_old.nim index 1f149694b..378003f4e 100644 --- a/tests/js/tjsffi_old.nim +++ b/tests/js/tjsffi_old.nim @@ -35,6 +35,9 @@ true ## same as tjsffi, but this test uses the old names: importc and ## importcpp. This test is for backwards compatibility. +# xxx instead of maintaining this near-duplicate test file, just have tests +# that check that importc, importcpp, importjs work and remove this file. + import jsffi, jsconsole # Tests for JsObject @@ -276,8 +279,8 @@ block: block: type TestObject = object a: int - onWhatever: proc(e: int): int - proc handleWhatever(this: TestObject, e: int): int = + onWhatever: proc(e: int): int {.nimcall.} + proc handleWhatever(this: TestObject, e: int): int {.nimcall.} = e + this.a proc test(): bool = let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever)) @@ -318,7 +321,7 @@ block: on("click") do (e: Event): console.log e - jslib.on("reloaded") do: + jslib.on("reloaded") do (): console.log jsarguments[0] # this test case is different from the above, because diff --git a/tests/js/tjsnimscombined.nim b/tests/js/tjsnimscombined.nim new file mode 100644 index 000000000..4d3e6c453 --- /dev/null +++ b/tests/js/tjsnimscombined.nim @@ -0,0 +1 @@ +import std/jsffi diff --git a/tests/js/tjsnimscombined.nims b/tests/js/tjsnimscombined.nims new file mode 100644 index 000000000..01b93d3fa --- /dev/null +++ b/tests/js/tjsnimscombined.nims @@ -0,0 +1 @@ +# test the condition where both `js` and `nimscript` are defined (nimscript receives priority) diff --git a/tests/js/tlent.nim b/tests/js/tlent.nim new file mode 100644 index 000000000..2546e5b1d --- /dev/null +++ b/tests/js/tlent.nim @@ -0,0 +1,33 @@ +discard """ + output: ''' +hmm +100 +hmm +100 +''' +""" + +# #16800 + +type A = object + b: int +var t = A(b: 100) +block: + proc getValues: lent int = + echo "hmm" + result = t.b + echo getValues() +block: + proc getValues: lent int = + echo "hmm" + t.b + echo getValues() + +when false: # still an issue, #16908 + template main = + iterator fn[T](a:T): lent T = yield a + let a = @[10] + for b in fn(a): echo b + + static: main() + main() diff --git a/tests/js/tnativeexc.nim b/tests/js/tnativeexc.nim index ea371c1cd..8b2b43e8f 100644 --- a/tests/js/tnativeexc.nim +++ b/tests/js/tnativeexc.nim @@ -18,7 +18,7 @@ try: except JsEvalError: doAssert false except JsSyntaxError as se: - doAssert se.message == "Unexpected token ; in JSON at position 0" + doAssert se.message == "Unexpected token ';', \";;\" is not valid JSON" except JsError as e: doAssert false diff --git a/tests/js/tneginthash.nim b/tests/js/tneginthash.nim new file mode 100644 index 000000000..c082405c9 --- /dev/null +++ b/tests/js/tneginthash.nim @@ -0,0 +1,21 @@ +# issue #19929 + +import std/[tables, hashes] + +type Foo = object + a: int + +proc hash(f: Foo): Hash = + var h: Hash = 0 + h = h !& hash(f.a) + result = !$h + +proc transpose[T, S](data: array[T, S]): Table[S, T] = + for i, x in data: + result[x] = i + +const xs = [Foo(a: 5), Foo(a: -5)] +const x = transpose(xs) + +doAssert x[Foo(a: -5)] == 1 +doAssert x[Foo(a: 5)] == 0 diff --git a/tests/js/tnilstrs.nim b/tests/js/tnilstrs.nim index c0048cb24..6c1e4e401 100644 --- a/tests/js/tnilstrs.nim +++ b/tests/js/tnilstrs.nim @@ -14,4 +14,12 @@ block: var x = "foo".cstring var y: string add(y, x) - doAssert y == "foo" \ No newline at end of file + doAssert y == "foo" + +block: + type Foo = object + a: string + var foo = Foo(a: "foo") + var y = move foo.a + doAssert foo.a.len == 0 + doAssert y == "foo" diff --git a/tests/js/tos.nim b/tests/js/tos.nim index 07eb3aaa3..40fb52bcf 100644 --- a/tests/js/tos.nim +++ b/tests/js/tos.nim @@ -1,3 +1,5 @@ +# xxx consider merging this in tests/stdlib/tos.nim for increased coverage (with selecting disabling) + static: doAssert defined(nodejs) import os @@ -11,37 +13,9 @@ block: doAssert not "foo".isAbsolute doAssert relativePath("", "bar") == "" doAssert normalizedPath(".///foo//./") == "foo" - let cwd = getCurrentDir() - - let isWindows = '\\' in cwd - # defined(windows) doesn't work with -d:nodejs but should - # these actually break because of that (see https://github.com/nim-lang/Nim/issues/13469) - if not isWindows: - doAssert cwd.isAbsolute - doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo" - -import std/sequtils - -template main = - putEnv("foo", "bar") - doAssert getEnv("foo") == "bar" - doAssert existsEnv("foo") - putEnv("foo", "") - doAssert existsEnv("foo") - putEnv("foo", "bar2") - doAssert getEnv("foo") == "bar2" - - when nimvm: - discard + when nimvm: discard else: - # need support in vmops: envPairs, delEnv - let s = toSeq(envPairs()) - doAssert ("foo", "bar2") in s - doAssert ("foo", "bar") notin s - - delEnv("foo") - doAssert not existsEnv("foo") - -static: main() -main() + let cwd = getCurrentDir() + doAssert cwd.isAbsolute + doAssert relativePath(getCurrentDir() / "foo", "bar") == ".." / "foo" diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim index c63b64d3a..a562ad63b 100644 --- a/tests/js/trepr.nim +++ b/tests/js/trepr.nim @@ -283,7 +283,7 @@ block bunch: result[] = b var - aa: A + aa = default(A) bb: B = B(a: "inner", b: @['o', 'b', 'j']) cc: A = A(a: 12, b: 1, c: 1.2, d: '\0', e: eC, f: "hello", g: {'A'}, h: {2'i16}, @@ -303,7 +303,7 @@ g = {}, h = {}, i = ["", "", ""], j = @[], -k = 0, +k = -12, l = [a = "", b = @[]], m = nil, diff --git a/tests/js/tseqops.nim b/tests/js/tseqops.nim index af664222c..8cfb50886 100644 --- a/tests/js/tseqops.nim +++ b/tests/js/tseqops.nim @@ -1,11 +1,3 @@ -discard """ - output: '''(x: 0, y: 0) -(x: 5, y: 0) -@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)] -@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)] -''' -""" - # bug #4139 type @@ -17,8 +9,8 @@ proc onLoad() = var foo = TestO(x: 0, y: 0) test.add(foo) foo.x = 5 - echo(test[0]) - echo foo + doAssert $test[0] == "(x: 0, y: 0)" + doAssert $foo == "(x: 5, y: 0)" onLoad() @@ -34,7 +26,7 @@ proc foo(x: var seq[MyObj]) = var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)] foo(s) -echo s +doAssert $s == """@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)]""" # bug #5933 import sequtils @@ -48,4 +40,9 @@ var test = @[Test(a: "1", b: 1), Test(a: "2", b: 2)] test.insert(@[Test(a: "3", b: 3)], 0) -echo test +doAssert $test == """@[(a: "3", b: 3), (a: "1", b: 1), (a: "2", b: 2)]""" + +proc hello(): array[5, int] = discard +var x = @(hello()) +x.add(2) +doAssert x == @[0, 0, 0, 0, 0, 2] diff --git a/tests/js/tsourcemap.nim b/tests/js/tsourcemap.nim new file mode 100644 index 000000000..d358e4a57 --- /dev/null +++ b/tests/js/tsourcemap.nim @@ -0,0 +1,96 @@ +discard """ + action: "run" + targets: "js" + cmd: "nim js -r -d:nodejs $options --sourceMap:on $file" +""" +import std/[os, json, strutils, sequtils, algorithm, assertions, paths, compilesettings] + +# Implements a very basic sourcemap parser and then runs it on itself. +# Allows to check for basic problems such as bad counts and lines missing (e.g. issue #21052) + +type + SourceMap = object + version: int + sources: seq[string] + names: seq[string] + mappings: string + file: string + + Line = object + line, column: int + file: string + +const + flag = 1 shl 5 + signBit = 0b1 + fourBits = 0b1111 + fiveBits = 0b11111 + mask = (1 shl 5) - 1 + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +var b64Table: seq[int] = 0.repeat(max(alphabet.mapIt(it.ord)) + 1) +for i, b in alphabet.pairs: + b64Table[b.ord] = i + +# From https://github.com/juancarlospaco/nodejs/blob/main/src/nodejs/jsfs.nim +proc importFs*() {.importjs: "var fs = require(\"fs\");".} +proc readFileSync*(path: cstring): cstring {.importjs: "(fs.$1(#).toString())".} +importFS() +# Read in needed files +let + jsFileName = string(querySetting(outDir).Path / "tsourcemap.js".Path) + mapFileName = jsFileName & ".map" + + data = parseJson($mapFileName.cstring.readFileSync()).to(SourceMap) + jsFile = $readFileSync(jsFileName.cstring) + +proc decodeVLQ(inp: string): seq[int] = + var + shift, value: int + for v in inp.mapIt(b64Table[it.ord]): + value += (v and mask) shl shift + if (v and flag) > 0: + shift += 5 + continue + result &= (value shr 1) * (if (value and 1) > 0: -1 else: 1) + shift = 0 + value = 0 + + +# Keep track of state +var + line = 0 + source = 0 + name = 0 + column = 0 + jsLine = 1 + lines: seq[Line] + +for gline in data.mappings.split(';'): + jsLine += 1 + var jsColumn = 0 + for item in gline.strip().split(','): + let value = item.decodeVLQ() + doAssert value.len in [0, 1, 4, 5] + if value.len == 0: + continue + jsColumn += value[0] + if value.len >= 4: + source += value[1] + line += value[2] + column += value[3] + lines &= Line(line: line, column: column, file: data.sources[source]) + +let jsLines = jsFile.splitLines().len +# There needs to be a mapping for every line in the JS +# If there isn't then the JS lines wont match up with Nim lines. +# Except we don't care about the final line since that doesn't need to line up +doAssert data.mappings.count(';') == jsLines - 1 + +# Check we can find this file somewhere in the source map +var foundSelf = false +for line in lines: + if "tsourcemap.nim" in line.file: + foundSelf = true + doAssert line.line in 0..<jsLines, "Lines is out of bounds for file" +doAssert foundSelf, "Couldn't find tsourcemap.nim in source map" diff --git a/tests/js/tstdlib_imports.nim b/tests/js/tstdlib_imports.nim index 7611b7dcb..db851ba28 100644 --- a/tests/js/tstdlib_imports.nim +++ b/tests/js/tstdlib_imports.nim @@ -4,49 +4,56 @@ discard """ {.warning[UnusedImport]: off.} +when defined(nimPreviewSlimSystem): + import std/[ + syncio, assertions, formatfloat, objectdollar, widestrs + ] + import std/[ # Core: bitops, typetraits, lenientops, macros, volatile, typeinfo, - # fails: endians, rlocks - # works but shouldn't: cpuinfo, locks + # fails due to FFI: rlocks + # fails due to cstring cast/copyMem: endians + # works but uses FFI: cpuinfo, locks # Algorithms: - algorithm, sequtils, + algorithm, enumutils, sequtils, setutils, # Collections: critbits, deques, heapqueue, intsets, lists, options, sets, - sharedlist, tables, - # fails: sharedtables + tables, packedsets, # Strings: cstrutils, editdistance, wordwrap, parseutils, ropes, - pegs, punycode, strformat, strmisc, strscans, strtabs, + pegs, strformat, strmisc, strscans, strtabs, strutils, unicode, unidecode, - # fails: encodings + # fails due to FFI: encodings # Time handling: monotimes, times, # Generic operator system services: os, streams, - # fails: distros, dynlib, marshal, memfiles, osproc, terminal + # fails intentionally: dynlib, marshal, memfiles + # fails due to FFI: osproc, terminal + # fails due to osproc import: distros # Math libraries: - complex, math, mersenne, random, rationals, stats, sums, - # works but shouldn't: fenv + complex, math, random, rationals, stats, sums, sysrand, + # works but uses FFI: fenv # Internet protocols: cookies, httpcore, mimetypes, uri, - # fails: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, - # asyncnet, cgi, httpclient, nativesockets, net, selectors, smtp - # works but shouldn't test: asyncstreams, asyncfutures + # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, + # asyncnet, cgi, httpclient, nativesockets, net, selectors + # works but no need to test: asyncstreams, asyncfutures # Threading: - # fails: threadpool + # fails due to FFI: threadpool # Parsers: htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, - # fails: parseopt + parseopt, jsonutils, # XML processing: xmltree, xmlparser, @@ -56,16 +63,18 @@ import std/[ # Hashing: base64, hashes, - # fails: md5, oids, sha1 + # fails due to cstring cast/endians import: oids + # fails due to copyMem/endians import: sha1 # Miscellaneous: - colors, logging, sugar, unittest, varints, - # fails: browsers, coro - # works but shouldn't: segfaults + colors, logging, sugar, unittest, varints, enumerate, with, + # fails due to FFI: browsers, coro + # works but uses FFI: segfaults # Modules for JS backend: - asyncjs, dom, jsconsole, jscore, jsffi, + asyncjs, dom, jsconsole, jscore, jsffi, jsbigints, # Unlisted in lib.html: - decls, compilesettings, with, wrapnils + decls, compilesettings, wrapnils, exitprocs, effecttraits, + genasts, importutils, isolation, jsfetch, jsformdata, jsheaders ] diff --git a/tests/js/tstdlib_various.nim b/tests/js/tstdlib_various.nim index d19f40c39..1e584f735 100644 --- a/tests/js/tstdlib_various.nim +++ b/tests/js/tstdlib_various.nim @@ -29,7 +29,7 @@ Hi Andreas! How do you feel, Rumpf? """ import - critbits, cstrutils, sets, strutils, tables, random, algorithm, ropes, + critbits, sets, strutils, tables, random, algorithm, ropes, lists, htmlgen, xmltree, strtabs @@ -150,12 +150,7 @@ block tsplit2: s.add("#") s.add(w) - var errored = false - try: - discard "hello".split("") - except AssertionError: - errored = true - doAssert errored + doAssert "true".split("") == @["true"] block txmlgen: var nim = "Nim" @@ -177,18 +172,3 @@ block txmltree: ]) ]) doAssert(y.innerText == "foobar") - - - -block tcstrutils: - let s = cstring "abcdef" - doAssert s.startsWith("a") - doAssert not s.startsWith("b") - doAssert s.endsWith("f") - doAssert not s.endsWith("a") - - let a = cstring "abracadabra" - doAssert a.startsWith("abra") - doAssert not a.startsWith("bra") - doAssert a.endsWith("abra") - doAssert not a.endsWith("dab") diff --git a/tests/js/ttypedarray.nim b/tests/js/ttypedarray.nim new file mode 100644 index 000000000..4807cb103 --- /dev/null +++ b/tests/js/ttypedarray.nim @@ -0,0 +1,26 @@ +discard """ + matrix: "--jsbigint64:off -d:nimStringHash2; --jsbigint64:on" +""" + +import std/private/jsutils + +proc main()= + template fn(a): untyped = jsConstructorName(a) + doAssert fn(array[2, int8].default) == "Int8Array" + doAssert fn(array[2, uint8].default) == "Uint8Array" + doAssert fn(array[2, byte].default) == "Uint8Array" + doAssert fn(array[2, char].default) == "Uint8Array" + whenJsNoBigInt64: discard + do: + doAssert fn(array[2, uint64].default) == "BigUint64Array" + doAssert fn([1'u8]) == "Uint8Array" + doAssert fn([1'u16]) == "Uint16Array" + doAssert fn([byte(1)]) == "Uint8Array" + doAssert fn([1.0'f32]) == "Float32Array" + doAssert fn(array[2, float32].default) == "Float32Array" + doAssert fn(array[2, float].default) == "Float64Array" + doAssert fn(array[2, float64].default) == "Float64Array" + doAssert fn([1.0]) == "Float64Array" + doAssert fn([1.0'f64]) == "Float64Array" + +main() diff --git a/tests/js/tunittest_error2.nim b/tests/js/tunittest_error2.nim new file mode 100644 index 000000000..9c5af7529 --- /dev/null +++ b/tests/js/tunittest_error2.nim @@ -0,0 +1,16 @@ +discard """ + exitcode: 1 + outputsub: ''' +[<foreign exception>] +[FAILED] Bad test + ''' + matrix: "-d:nodejs" + targets: "js" + joinable: false +""" + +# bug #16978 +import unittest +test "Bad test": + var x: cstring = nil + let y = x[0] |