summary refs log tree commit diff stats
path: root/tests/js
diff options
context:
space:
mode:
Diffstat (limited to 'tests/js')
-rw-r--r--tests/js/readme.md5
-rw-r--r--tests/js/t11166.nim18
-rw-r--r--tests/js/t17177.nim10
-rw-r--r--tests/js/t20233.nim7
-rw-r--r--tests/js/t20235.nim11
-rw-r--r--tests/js/t21209.nim6
-rw-r--r--tests/js/t21209.nims1
-rw-r--r--tests/js/t21247.nim15
-rw-r--r--tests/js/t21439.nim11
-rw-r--r--tests/js/t7109.nim8
-rw-r--r--tests/js/t8821.nim5
-rw-r--r--tests/js/t9410.nim4
-rw-r--r--tests/js/tasync.nim33
-rw-r--r--tests/js/tasyncjs.nim107
-rw-r--r--tests/js/tasyncjs_bad.nim22
-rw-r--r--tests/js/tasyncjs_pragma.nim (renamed from tests/js/tasync_pragma.nim)2
-rw-r--r--tests/js/tbasics.nim2
-rw-r--r--tests/js/tbigint_backend.nim57
-rw-r--r--tests/js/tbyvar.nim4
-rw-r--r--tests/js/tclosures.nim2
-rw-r--r--tests/js/tcodegendeclproc.nim2
-rw-r--r--tests/js/tcsymbol.nim6
-rw-r--r--tests/js/tdanger.nim17
-rw-r--r--tests/js/tfieldchecks.nim2
-rw-r--r--tests/js/tindexdefect.nim9
-rw-r--r--tests/js/tjsffi.nim435
-rw-r--r--tests/js/tjsffi_old.nim9
-rw-r--r--tests/js/tjsnimscombined.nim1
-rw-r--r--tests/js/tjsnimscombined.nims1
-rw-r--r--tests/js/tlent.nim33
-rw-r--r--tests/js/tnativeexc.nim2
-rw-r--r--tests/js/tneginthash.nim21
-rw-r--r--tests/js/tnilstrs.nim10
-rw-r--r--tests/js/tos.nim38
-rw-r--r--tests/js/trepr.nim4
-rw-r--r--tests/js/tseqops.nim21
-rw-r--r--tests/js/tsourcemap.nim96
-rw-r--r--tests/js/tstdlib_imports.nim51
-rw-r--r--tests/js/tstdlib_various.nim24
-rw-r--r--tests/js/ttypedarray.nim26
-rw-r--r--tests/js/tunittest_error2.nim16
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]