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.nim20
-rw-r--r--tests/js/t11353.nim14
-rw-r--r--tests/js/t11354.nim20
-rw-r--r--tests/js/t11697.nim5
-rw-r--r--tests/js/t12223.nim20
-rw-r--r--tests/js/t12303.nim16
-rw-r--r--tests/js/t12672.nim12
-rw-r--r--tests/js/t14153.nim22
-rw-r--r--tests/js/t14570.nim11
-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/t6612.nim24
-rw-r--r--tests/js/t7109.nim8
-rw-r--r--tests/js/t7127.nim2
-rw-r--r--tests/js/t7224.nim28
-rw-r--r--tests/js/t7249.nim21
-rw-r--r--tests/js/t7534.nim7
-rw-r--r--tests/js/t8231.nim3
-rw-r--r--tests/js/t8821.nim9
-rw-r--r--tests/js/t8914.nim12
-rw-r--r--tests/js/t9410.nim471
-rw-r--r--tests/js/tarrayboundscheck.nim50
-rw-r--r--tests/js/tasyncjs.nim107
-rw-r--r--tests/js/tasyncjs_bad.nim22
-rw-r--r--tests/js/tasyncjs_pragma.nim29
-rw-r--r--tests/js/tbasics.nim62
-rw-r--r--tests/js/tbigint_backend.nim57
-rw-r--r--tests/js/tbyvar.nim123
-rw-r--r--tests/js/tclosures.nim95
-rw-r--r--tests/js/tcodegendeclproc.nim11
-rw-r--r--tests/js/tcodegendeclvar.nim10
-rw-r--r--tests/js/tconsole.nim13
-rw-r--r--tests/js/tcopying.nim80
-rw-r--r--tests/js/tcsymbol.nim6
-rw-r--r--tests/js/tdanger.nim17
-rw-r--r--tests/js/tderef.nim20
-rw-r--r--tests/js/tdiscard.nim3
-rw-r--r--tests/js/tdollar_float.nim62
-rw-r--r--tests/js/temptyseq.nim8
-rw-r--r--tests/js/tenumhole.nim12
-rw-r--r--tests/js/tenumnegkey.nim12
-rw-r--r--tests/js/tenumoffset.nim19
-rw-r--r--tests/js/test1.nim52
-rw-r--r--tests/js/test2.nim58
-rw-r--r--tests/js/testmagic.nim12
-rw-r--r--tests/js/testobjs.nim73
-rw-r--r--tests/js/testtojsstr.nim8
-rw-r--r--tests/js/tfieldchecks.nim48
-rw-r--r--tests/js/tfloatround.nim7
-rw-r--r--tests/js/tglobal.nim30
-rw-r--r--tests/js/timplicit_nodecl.nim13
-rw-r--r--tests/js/tindexdefect.nim9
-rw-r--r--tests/js/tjsffi.nim274
-rw-r--r--tests/js/tjsffi_old.nim340
-rw-r--r--tests/js/tjshello.nim10
-rw-r--r--tests/js/tjshello_stacktrace.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/tmangle.nim106
-rw-r--r--tests/js/tmodify_cstring.nim6
-rw-r--r--tests/js/tnativeexc.nim31
-rw-r--r--tests/js/tneginthash.nim21
-rw-r--r--tests/js/tnilstrs.nim25
-rw-r--r--tests/js/tobjfieldbyvar.nim20
-rw-r--r--tests/js/tos.nim21
-rw-r--r--tests/js/trefbyvar.nim69
-rw-r--r--tests/js/trepr.nim413
-rw-r--r--tests/js/treprinifexpr.nim18
-rw-r--r--tests/js/tseqops.nim48
-rw-r--r--tests/js/tsourcemap.nim96
-rw-r--r--tests/js/tstdlib_imports.nim80
-rw-r--r--tests/js/tstdlib_various.nim174
-rw-r--r--tests/js/tstreams.nim22
-rw-r--r--tests/js/tstring_assignment.nim21
-rw-r--r--tests/js/tstringitems.nim95
-rw-r--r--tests/js/ttempgen.nim79
-rw-r--r--tests/js/tthismangle.nim23
-rw-r--r--tests/js/ttryexceptnewsyntax.nim13
-rw-r--r--tests/js/ttypedarray.nim26
-rw-r--r--tests/js/tunion.nim7
-rw-r--r--tests/js/tunittest_error.nim24
-rw-r--r--tests/js/tunittest_error2.nim16
-rw-r--r--tests/js/tvarargs.nim15
-rw-r--r--tests/js/twritestacktrace.nim12
91 files changed, 4068 insertions, 0 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
new file mode 100644
index 000000000..e98ccda10
--- /dev/null
+++ b/tests/js/t11166.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''
+test1
+test2
+'''
+"""
+
+import jsffi
+
+type
+  C = object
+    props: int
+
+var c: C
+
+when compiles(c.props):
+  echo "test1"
+
+when not compiles(d.props):
+  echo "test2"
diff --git a/tests/js/t11353.nim b/tests/js/t11353.nim
new file mode 100644
index 000000000..c1bc0ad4b
--- /dev/null
+++ b/tests/js/t11353.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+{}
+{}
+'''
+"""
+
+proc foo() =
+  var bar: set[int16] = {}
+  echo bar
+  bar.incl(1)
+
+foo()
+foo()
diff --git a/tests/js/t11354.nim b/tests/js/t11354.nim
new file mode 100644
index 000000000..8dee90de0
--- /dev/null
+++ b/tests/js/t11354.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''
+0
+@[@[0, 1]]
+'''
+"""
+
+type
+  TrackySeq[T] = object
+    s: seq[T]
+    pos: int
+
+proc foobar(ls: var TrackySeq[seq[int]], i: int): var seq[int] =
+  echo ls.pos  # removing this, or making the return explicit works
+  ls.s[i]
+
+var foo: TrackySeq[seq[int]]
+foo.s.add(@[0])
+foo.foobar(0).add(1)
+echo foo.s
\ No newline at end of file
diff --git a/tests/js/t11697.nim b/tests/js/t11697.nim
new file mode 100644
index 000000000..967752586
--- /dev/null
+++ b/tests/js/t11697.nim
@@ -0,0 +1,5 @@
+import tables
+
+var xs: Table[int, Table[int, int]]
+
+doAssertRaises(KeyError): reset xs[0]
diff --git a/tests/js/t12223.nim b/tests/js/t12223.nim
new file mode 100644
index 000000000..c0e75fb44
--- /dev/null
+++ b/tests/js/t12223.nim
@@ -0,0 +1,20 @@
+discard """
+  action: "run"
+  output: '''
+caught
+index out of bounds, the container is empty
+'''
+"""
+
+proc fun() =
+  var z: seq[string]
+  discard z[4]
+
+proc main()=
+  try:
+    fun()
+  except Exception as e:
+    echo "caught"
+    echo getCurrentExceptionMsg()
+
+main()
\ No newline at end of file
diff --git a/tests/js/t12303.nim b/tests/js/t12303.nim
new file mode 100644
index 000000000..270d82ced
--- /dev/null
+++ b/tests/js/t12303.nim
@@ -0,0 +1,16 @@
+discard """
+  output: "{ b: 2 }"
+"""
+
+import jsconsole, jsffi
+
+type
+  A = ref object
+   b: B
+
+  B = object
+    b: int
+
+var a = cast[A](js{})
+a.b = B(b: 2)
+console.log a.b
diff --git a/tests/js/t12672.nim b/tests/js/t12672.nim
new file mode 100644
index 000000000..a658fbcbe
--- /dev/null
+++ b/tests/js/t12672.nim
@@ -0,0 +1,12 @@
+discard """
+  output: ""
+"""
+
+proc foo =
+  var x: seq[seq[int]]
+  for row in x.mitems:
+    let i = 1
+    echo row
+    inc row[i-1]
+
+foo()
diff --git a/tests/js/t14153.nim b/tests/js/t14153.nim
new file mode 100644
index 000000000..350bbd83b
--- /dev/null
+++ b/tests/js/t14153.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+index 5 not in 0 .. 2
+index 5 not in 0 .. 2
+'''
+"""
+
+var x = @[1, 2, 3]
+
+try:
+  echo x[5]
+except IndexError:
+  echo getCurrentExceptionMsg()
+except:
+  doAssert false
+
+try:
+  x[5] = 8
+except IndexError:
+  echo getCurrentExceptionMsg()
+except:
+  doAssert false
diff --git a/tests/js/t14570.nim b/tests/js/t14570.nim
new file mode 100644
index 000000000..100c2651d
--- /dev/null
+++ b/tests/js/t14570.nim
@@ -0,0 +1,11 @@
+discard """
+  output: '''
+18
+'''
+"""
+
+type A = range[15 .. 30]
+
+let a: A = 18
+
+echo ord(a)
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/t6612.nim b/tests/js/t6612.nim
new file mode 100644
index 000000000..232711c16
--- /dev/null
+++ b/tests/js/t6612.nim
@@ -0,0 +1,24 @@
+discard """
+  action: "run"
+"""
+
+proc fillWith(sq: var seq[int], n: int, unused: string) =
+  sq = @[n]
+
+type
+  Object = object of RootObj
+    case hasNums: bool
+    of true:
+      numbers: seq[int]
+    of false:
+      discard
+    always: seq[int]
+
+var obj = Object(hasNums: true)
+
+obj.always.fillWith(5, "unused")
+doAssert obj.always == @[5]
+
+obj.numbers.fillWith(3, "unused")
+doAssert obj.numbers == @[3]
+doAssert obj.always == @[5]
diff --git a/tests/js/t7109.nim b/tests/js/t7109.nim
new file mode 100644
index 000000000..a1a3b718e
--- /dev/null
+++ b/tests/js/t7109.nim
@@ -0,0 +1,8 @@
+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/t7127.nim b/tests/js/t7127.nim
new file mode 100644
index 000000000..364aedd4a
--- /dev/null
+++ b/tests/js/t7127.nim
@@ -0,0 +1,2 @@
+doAssertRaises(DivByZeroDefect): discard 1 mod 0
+doAssertRaises(DivByZeroDefect): discard 1 div 0
\ No newline at end of file
diff --git a/tests/js/t7224.nim b/tests/js/t7224.nim
new file mode 100644
index 000000000..77fef10a7
--- /dev/null
+++ b/tests/js/t7224.nim
@@ -0,0 +1,28 @@
+discard """
+  cmd: "nim $target $options --stackTrace:on --lineTrace:on $file"
+  outputsub: '''
+t7224.nim(25) at module t7224
+t7224.nim(22) at t7224.aaa
+t7224.nim(19) at t7224.bbb
+t7224.nim(16) at t7224.ccc
+t7224.nim(13) at t7224.ddd
+'''
+"""
+
+proc ddd() =
+  raise newException(IOError, "didn't do stuff")
+
+proc ccc() =
+  ddd()
+
+proc bbb() =
+  ccc()
+
+proc aaa() =
+  bbb()
+
+try:
+  aaa()
+
+except IOError as e:
+  echo getStackTrace(e)
diff --git a/tests/js/t7249.nim b/tests/js/t7249.nim
new file mode 100644
index 000000000..52eee2f7c
--- /dev/null
+++ b/tests/js/t7249.nim
@@ -0,0 +1,21 @@
+discard """
+  output: '''
+a -> 2
+a <- 2
+'''
+"""
+
+import jsffi
+
+var a = JsAssoc[cstring, int]{a: 2}
+
+for z, b in a:
+  echo z, " -> ", b
+
+proc f =
+  var a = JsAssoc[cstring, int]{a: 2}
+
+  for z, b in a:
+    echo z, " <- ", b
+
+f()
diff --git a/tests/js/t7534.nim b/tests/js/t7534.nim
new file mode 100644
index 000000000..64aadb8d6
--- /dev/null
+++ b/tests/js/t7534.nim
@@ -0,0 +1,7 @@
+proc f(x: int): int =
+  result = case x
+    of 1: 2
+    elif x == 2: 3
+    else: 1
+
+doAssert 2 == f(f(f(f(1))))
diff --git a/tests/js/t8231.nim b/tests/js/t8231.nim
new file mode 100644
index 000000000..b0625a621
--- /dev/null
+++ b/tests/js/t8231.nim
@@ -0,0 +1,3 @@
+import strutils
+
+doAssert formatSize(2462056448, '.', bpIEC, false) == "2.293GiB"
\ No newline at end of file
diff --git a/tests/js/t8821.nim b/tests/js/t8821.nim
new file mode 100644
index 000000000..38c88efa8
--- /dev/null
+++ b/tests/js/t8821.nim
@@ -0,0 +1,9 @@
+
+proc isInt32(i: int): bool =
+  case i 
+  of 1 .. 70000:
+    return true
+  else:
+    return false
+
+doAssert isInt32(1) == true
\ No newline at end of file
diff --git a/tests/js/t8914.nim b/tests/js/t8914.nim
new file mode 100644
index 000000000..ff716b42a
--- /dev/null
+++ b/tests/js/t8914.nim
@@ -0,0 +1,12 @@
+discard """
+  output: '''
+@[42]
+@[24, 42]
+'''
+"""
+
+var x = @[42,4242]
+x.delete(1)
+echo x
+x.insert(24)
+echo x
diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim
new file mode 100644
index 000000000..042520dc5
--- /dev/null
+++ b/tests/js/t9410.nim
@@ -0,0 +1,471 @@
+template tests =
+  block:
+    var i = 0
+    i = 2
+
+    var y: ptr int
+    doAssert y == nil
+    doAssert isNil(y)
+    y = i.addr
+    y[] = 3
+    doAssert i == 3
+    doAssert i == y[]
+
+    let z = i.addr
+    z[] = 4
+    doAssert i == 4
+    doAssert i == y[] and y[] == z[]
+
+    var hmm = (a: (b: z))
+    var hmmptr = hmm.a.b.addr
+    hmmptr[][] = 5
+
+    doAssert i == 5
+    doAssert y == z
+    doAssert z == hmmptr[]
+    doAssert 5 == y[] and 5 == z[] and 5 == hmmptr[][]
+
+  block:
+    var someint = 500
+
+    let p: ptr int = someint.addr
+    let tup = (f: p)
+    let tcopy = tup
+    var vtcopy = tcopy
+    p[] = 654
+    doAssert p[] == 654
+    doAssert tup.f[] == 654
+    doAssert tcopy.f[] == 654
+    doAssert vtcopy.f[] == 654
+
+  block:
+    var someint = 500
+
+    var p: ptr int = someint.addr
+    let arr = [p]
+    let arrc = arr
+    p[] = 256
+    doAssert someint == 256
+    doAssert p[] == 256
+    doAssert arr[0][] == 256
+    doAssert arrc[0][] == 256
+
+  block:
+    var someref: ref int
+    new(someref)
+    var someref2 = someref
+
+    var tup1 = (f: someref)
+    tup1.f = someref
+    let tup2 = tup1
+
+    someref[] = 543
+
+    proc passref(r: var ref int): var ref int = r
+    new(passref(someref))
+
+    doAssert someref[] == 0
+    doAssert tup1.f[] == 543
+    doAssert tup2.f[] == 543
+    doAssert someref2[] == 543
+
+  block:
+    type Whatever = object
+      i: ref int
+
+    var someref: ref int
+    new(someref)
+    someref[] = 10
+
+    let w = Whatever(i: someref)
+    var wcopy = w
+
+    someref[] = 20
+
+    doAssert w.i[] == 20
+    doAssert someref[] == 20
+    doAssert wcopy.i[] == 20
+    doAssert w.i == wcopy.i
+    #echo w.i[], " ", someref[], " ", wcopy.i[]
+
+  block:
+    var oneseq: ref seq[ref int]
+    new(oneseq)
+    var aref: ref int
+    new(aref)
+    aref[] = 123
+    let arefs = [aref]
+    oneseq[] &= arefs[0]
+    oneseq[] &= aref
+    aref[] = 222
+    new(aref)
+    doAssert oneseq[0] == oneseq[1]
+    doAssert oneseq[0][] == 222
+    doAssert oneseq[1][] == 222
+    doAssert aref[] == 0
+
+  block:
+    var seqs: ref seq[ref seq[ref int]]
+    new(seqs)
+    seqs[] = newSeq[ref seq[ref int]](1)
+    new(seqs[0])
+    seqs[0][] = newSeq[ref int](0)
+
+    var aref: ref int
+    new aref
+    aref[] = 654
+
+    let arefs = [aref]
+    doAssert arefs[0] == aref
+    seqs[0][] &= arefs[0]
+    seqs[0][] &= aref
+    seqs[0][1][] = 456
+    let seqs2 = seqs
+    let same = seqs2[0][0] == seqs2[0][1]
+    doAssert arefs[0] == aref
+    doAssert aref[] == 456
+    doAssert seqs[].len == 1
+    doAssert seqs[0][].len == 2
+    doAssert seqs[0][0][] == 456
+    doAssert seqs[0][1][] == 456
+    doAssert same
+
+  block:
+    type Obj = object
+      x, y: int
+
+    var objrefs: seq[ref Obj] = @[(ref Obj)(nil), nil, nil]
+    objrefs[2].new
+    objrefs[2][] = Obj(x: 123, y: 321)
+    objrefs[1] = objrefs[2]
+    doAssert objrefs[0] == nil
+    doAssert objrefs[1].y == 321
+    doAssert objrefs[2].y == 321
+    doAssert objrefs[1] == objrefs[2]
+
+  block:
+    var refs: seq[ref string] = @[(ref string)(nil), nil, nil]
+    refs[1].new
+    refs[1][] = "it's a ref!"
+    refs[0] = refs[1]
+    refs[2] = refs[1]
+    new(refs[0])
+    doAssert refs[0][] == ""
+    doAssert refs[1][] == "it's a ref!"
+    doAssert refs[2][] == "it's a ref!"
+    doAssert refs[1] == refs[2]
+
+  block:
+    var retaddr_calls = 0
+    proc retaddr(p: var int): var int =
+      retaddr_calls += 1
+      p
+
+    var tfoo_calls = 0
+    proc tfoo(x: var int) =
+      tfoo_calls += 1
+      x += 10
+      var y = x.addr
+      y[] += 20
+      retaddr(x) += 30
+      let z = retaddr(x).addr
+      z[] += 40
+
+    var ints = @[1, 2, 3]
+    tfoo(ints[1])
+    doAssert retaddr_calls == 2
+    doAssert tfoo_calls == 1
+    doAssert ints[1] == 102
+
+    var tbar_calls = 0
+    proc tbar(x: var int): var int =
+      tbar_calls += 1
+      x
+
+    tbar(ints[2]) += 10
+    tbar(ints[2]) *= 2
+    doAssert tbar_calls == 2
+
+    var tqux_calls = 0
+    proc tqux(x: var int): ptr int =
+      tqux_calls += 1
+      x.addr
+
+    discard tqux(ints[2]) == tqux(ints[2])
+    doAssert tqux_calls == 2
+    doAssert isNil(tqux(ints[2])) == false
+    doAssert tqux_calls == 3
+
+    var tseq_calls = 0
+    proc tseq(x: var seq[int]): var seq[int] =
+      tseq_calls += 1
+      x
+
+    tseq(ints) &= 999
+    doAssert tseq_calls == 1
+    doAssert ints == @[1, 102, 26, 999]
+
+    var rawints = @[555]
+    rawints &= 666
+    doAssert rawints == @[555, 666]
+
+    var resetints_calls = 0
+    proc resetInts(): int =
+      resetints_calls += 1
+      ints = @[0, 0, 0]
+      1
+
+    proc incr(x: var int; b: int): var int =
+      x = x + b
+      x
+
+    var q = 0
+    var qp = q.addr
+    qp[] += 123
+    doAssert q == 123
+    # check order of evaluation
+    doAssert (resetInts() + incr(q, tqux(ints[2])[])) == 124
+
+  block: # reset
+    var calls = 0
+    proc passsomething(x: var int): var int =
+      calls += 1
+      x
+
+    var
+      a = 123
+      b = 500
+      c = a.addr
+    reset(passsomething(a))
+    doAssert calls == 1
+    reset(b)
+    doAssert a == b
+    reset(c)
+    doAssert c == nil
+
+  block: # strings
+    var calls = 0
+    proc stringtest(s: var string): var string =
+      calls += 1
+      s
+
+    var somestr: string
+
+    stringtest(somestr) &= 'a'
+    stringtest(somestr) &= 'b'
+    doAssert calls == 2
+    doAssert somestr == "ab"
+    stringtest(somestr) &= "woot!"
+    doAssert somestr == "abwoot!"
+    doAssert calls == 3
+
+    doAssert stringtest(somestr).len == 7
+    doAssert calls == 4
+    doAssert high(stringtest(somestr)) == 6
+    doAssert calls == 5
+
+    var somestr2: string
+    stringtest(somestr2).setLen(stringtest(somestr).len)
+    doAssert calls == 7
+    doAssert somestr2.len == somestr.len
+
+    var somestr3: string
+    doAssert (somestr3 & "foo") == "foo"
+
+    block:
+      var a, b, c, d: string
+      d = a & b & c
+      doAssert d == ""
+      d = stringtest(a) & stringtest(b) & stringtest(c)
+      doAssert calls == 10
+      doAssert d == ""
+
+  block: # seqs
+    var calls = 0
+    proc seqtest(s: var seq[int]): var seq[int] =
+      calls += 1
+      s
+
+    var someseq: seq[int]
+
+    seqtest(someseq) &= 1
+    seqtest(someseq) &= 2
+    doAssert calls == 2
+    doAssert someseq == @[1, 2]
+    seqtest(someseq) &= @[3, 4, 5]
+    doAssert someseq == @[1, 2, 3, 4, 5]
+    doAssert calls == 3
+
+    doAssert seqtest(someseq).len == 5
+    doAssert calls == 4
+    doAssert high(seqtest(someseq)) == 4
+    doAssert calls == 5
+
+    # genArrayAddr
+    doAssert seqtest(someseq)[2] == 3
+    doAssert calls == 6
+
+    seqtest(someseq).setLen(seqtest(someseq).len)
+    doAssert calls == 8
+
+    var somenilseq: seq[int]
+    seqtest(somenilseq).setLen(3)
+    doAssert calls == 9
+    doAssert somenilseq[1] == 0
+
+    someseq = @[1, 2, 3]
+    doAssert (seqtest(someseq) & seqtest(someseq)) == @[1, 2, 3, 1, 2, 3]
+
+
+  block: # mInc, mDec
+    var calls = 0
+    proc someint(x: var int): var int =
+      calls += 1
+      x
+
+    var x = 10
+
+    inc(someint(x))
+    doAssert x == 11
+    doAssert calls == 1
+
+    dec(someint(x))
+    doAssert x == 10
+    doAssert calls == 2
+
+  block: # uints
+    var calls = 0
+    proc passuint(x: var uint32): var uint32 =
+      calls += 1
+      x
+
+    var u: uint32 = 5
+    passuint(u) += 1
+    doAssert u == 6
+    doAssert calls == 1
+
+    passuint(u) -= 1
+    doAssert u == 5
+    doAssert calls == 2
+
+    passuint(u) *= 2
+    doAssert u == 10
+    doAssert calls == 3
+
+  block: # objs
+    type Thing = ref object
+      x, y: int
+
+    var a, b: Thing
+    a = Thing()
+    b = a
+
+    doAssert a == b
+
+    var calls = 0
+    proc passobj(o: var Thing): var Thing =
+      calls += 1
+      o
+
+    passobj(b) = Thing(x: 123)
+    doAssert calls == 1
+    doAssert a != b
+    doAssert b.x == 123
+
+    var passobjptr_calls = 0
+    proc passobjptr(o: var Thing): ptr Thing =
+      passobjptr_calls += 1
+      o.addr
+
+    passobjptr(b)[] = Thing(x: 234)
+    doAssert passobjptr_calls == 1
+    doAssert a != b
+    doAssert b.x == 234
+    passobjptr(b)[].x = 500
+    doAssert b.x == 500
+
+    var pptr = passobjptr(b)
+    pptr.x += 100
+    doAssert b.x == 600
+
+    proc getuninitptr(): ptr int =
+      return
+
+    doAssert getuninitptr() == nil
+
+  block: # pointer casting
+    var obj = (x: 321, y: 543)
+    var x = 500
+
+    var objptr = obj.addr
+    var xptr = x.addr
+
+    var p1, p2: pointer
+    p1 = cast[pointer](objptr)
+    p2 = cast[pointer](xptr)
+    doAssert p1 != p2
+
+    p1 = cast[pointer](objptr)
+    p2 = cast[pointer](objptr)
+    doAssert p1 == p2
+
+    let objptr2 = cast[type(objptr)](p2)
+    doAssert objptr == objptr2
+
+    p1 = cast[pointer](xptr)
+    p2 = cast[pointer](xptr)
+    doAssert p1 == p2
+
+    let xptr2 = cast[type(xptr)](p2)
+    doAssert xptr == xptr2
+  
+  block: # var types
+    block t10202:
+      type Point = object
+        x: float
+        y: float
+
+      var points: seq[Point]
+
+      points.add(Point(x:1, y:2))
+
+      for i, p in points.mpairs:
+        p.x += 1
+
+      doAssert points[0].x == 2
+    
+    block:
+      var ints = @[1, 2, 3]
+      for i, val in mpairs ints:
+        val *= 10
+      doAssert ints == @[10, 20, 30]
+    
+    block:
+      var seqOfSeqs = @[@[1, 2], @[3, 4]]
+      for i, val in mpairs seqOfSeqs:
+        val[0] *= 10
+      doAssert seqOfSeqs == @[@[10, 2], @[30, 4]]
+
+  when false:
+    block: # openArray
+          # Error: internal error: genAddr: nkStmtListExpr
+      var calls = 0
+      proc getvarint(x: var openArray[int]): var int =
+        calls += 1
+        if true:
+          x[1]
+        else:
+          x[0]
+
+      var arr = [1, 2, 3]
+      getvarint(arr) += 5
+      doAssert calls == 1
+      doAssert arr[1] == 7
+
+proc tests_in_proc =
+  tests
+
+# since pointers are handled differently in global/local contexts
+# let's just run all of them twice
+tests_in_proc()
+tests
diff --git a/tests/js/tarrayboundscheck.nim b/tests/js/tarrayboundscheck.nim
new file mode 100644
index 000000000..d8bf8de97
--- /dev/null
+++ b/tests/js/tarrayboundscheck.nim
@@ -0,0 +1,50 @@
+discard """
+  output: '''idx out of bounds: -1
+month out of bounds: 0
+Jan
+Feb
+Mar
+Apr
+May
+Jun
+Jul
+Aug
+Sep
+Oct
+Nov
+Dec
+month out of bounds: 13
+idx out of bounds: 14
+'''
+"""
+
+{.push boundChecks:on.}
+
+# see issue #6532:
+# js backend 0.17.3: array bounds check for non zero based arrays is buggy
+
+proc test_arrayboundscheck() =
+  var months: array[1..12, string] =
+    ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
+     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
+  var indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13]
+
+  for i in -1 .. 14:
+    try:
+      let idx = indices[i]
+      try:
+        echo months[idx]
+      except:
+        echo "month out of bounds: ", idx
+    except:
+      echo "idx out of bounds: ", i
+  
+  # #13966
+  var negativeIndexed: array[-2..2, int] = [0, 1, 2, 3, 4]
+  negativeIndexed[-1] = 2
+  negativeIndexed[1] = 2
+  doAssert negativeIndexed == [0, 2, 2, 2, 4]
+
+test_arrayboundscheck()
+{.pop.}
\ No newline at end of file
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/tasyncjs_pragma.nim b/tests/js/tasyncjs_pragma.nim
new file mode 100644
index 000000000..2b6f32e92
--- /dev/null
+++ b/tests/js/tasyncjs_pragma.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''
+0
+t
+'''
+"""
+
+# xxx merge into tasyncjs.nim
+
+import asyncjs, macros
+
+macro f*(a: untyped): untyped =
+  assert a.kind == nnkProcDef
+  result = nnkProcDef.newTree(a.name, a[1], a[2], a.params, a.pragma, a[5], nnkStmtList.newTree())
+  let call = quote:
+    echo 0
+  result.body.add(call)
+  for child in a.body:
+    result.body.add(child)
+  #echo result.body.repr
+
+proc t* {.async, f.} =
+  echo "t"
+
+proc t0* {.async.} =
+  await t()
+
+discard t0()
+
diff --git a/tests/js/tbasics.nim b/tests/js/tbasics.nim
new file mode 100644
index 000000000..ef84f8bb5
--- /dev/null
+++ b/tests/js/tbasics.nim
@@ -0,0 +1,62 @@
+discard """
+  output: '''ABCDC
+1
+14
+ok
+1'''
+"""
+
+type
+  MyEnum = enum
+    A,B,C,D
+# trick the optimizer with an seq:
+var x = @[A,B,C,D]
+echo x[0],x[1],x[2],x[3],MyEnum(2)
+
+# bug #10651
+
+var xa: seq[int]
+var ya = @[1,2]
+xa &= ya
+echo xa[0]
+
+proc test =
+  var yup: seq[int]
+  try:
+    yup.add 14
+    echo yup.pop
+  finally:
+    discard
+
+test()
+
+when true:
+  var a: seq[int]
+
+  a.setLen(0)
+
+  echo "ok"
+
+# bug #10697
+proc test2 =
+  var val = uint16(0)
+  var i = 0
+  if i < 2:
+    val += uint16(1)
+  echo int(val)
+
+test2()
+
+
+var someGlobal = default(array[5, int])
+for x in someGlobal: doAssert(x == 0)
+
+proc tdefault =
+  var x = default(int)
+  doAssert(x == 0)
+  proc inner(v: openArray[string]) =
+    doAssert(v.len == 0)
+
+  inner(default(seq[string]))
+
+tdefault()
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
new file mode 100644
index 000000000..93724a2f1
--- /dev/null
+++ b/tests/js/tbyvar.nim
@@ -0,0 +1,123 @@
+discard """
+  output: '''
+foo 12
+bar 12
+2
+foo 12
+bar 12
+2
+12.5
+(nums: @[5.0, 5.0, 10.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+(nums: @[5.0, 5.0, 50.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+(nums: @[5.0, 5.0, 45.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+(nums: @[5.0, 5.0, 9.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0])
+asd
+'''
+"""
+
+# bug #1489
+proc foo(x: int) = echo "foo ", x
+proc bar(y: var int) = echo "bar ", y
+
+var x = 12
+foo(x)
+bar(x)
+
+# bug #1490
+var y = 1
+y *= 2
+echo y
+
+proc main =
+  var x = 12
+  foo(x)
+  bar(x)
+
+  var y = 1
+  y *= 2
+  echo y
+
+main()
+
+# Test: pass var seq to var openArray
+var s = @[2, 1]
+proc foo(a: var openArray[int]) = a[0] = 123
+
+proc bar(s: var seq[int], a: int) =
+  doAssert(a == 5)
+  foo(s)
+s.bar(5)
+doAssert(s == @[123, 1])
+
+import tables
+block: # Test get addr of byvar return value
+  var t = initTable[string, int]()
+  t["hi"] = 5
+  let a = addr t["hi"]
+  a[] = 10
+  doAssert(t["hi"] == 10)
+
+block: # Test var arg inside case expression. #5244
+  proc foo(a: var string) =
+    a = case a
+    of "a": "error"
+    of "b": "error"
+    else: a
+  var a = "ok"
+  foo(a)
+  doAssert(a == "ok")
+
+
+proc mainowar =
+  var x = 9.0
+  x += 3.5
+  echo x
+
+mainowar()
+
+
+# bug #5608
+
+type Foo = object
+    nums : seq[float]
+
+proc newFoo(len : int, default = 0.0) : Foo =
+    result = Foo()
+    result.nums = newSeq[float](len)
+    for i in 0..(len - 1):
+        result.nums[i] = default
+
+proc `[]=`(f : var Foo, i : int, v : float) =
+    f.nums[i] = v
+
+proc `[]`(f : Foo, i : int) : float = f.nums[i]
+
+proc `[]`(f : var Foo, i : int) : var float = f.nums[i]
+
+var f = newFoo(10,5)
+
+f[2] += 5
+echo f
+f[2] *= 5
+echo f
+f[2] -= 5
+echo f
+f[2] /= 5
+echo f
+
+# regression for #5608
+import tables
+
+type
+  SomeObj = ref object
+    s: cstring
+
+var a = initTable[cstring, Table[cstring, SomeObj]]()
+
+var b = initTable[cstring, SomeObj]()
+
+b.add(cstring"b", SomeObj(s: cstring"asd"))
+
+a.add(cstring"a", b)
+
+echo a[cstring"a"][cstring"b"].s
diff --git a/tests/js/tclosures.nim b/tests/js/tclosures.nim
new file mode 100644
index 000000000..4f1c28de3
--- /dev/null
+++ b/tests/js/tclosures.nim
@@ -0,0 +1,95 @@
+discard """
+  action: run
+"""
+
+import random, strutils
+const consolePrefix = "jsCallbacks"
+
+asm """
+    var callback = []
+    function regCallback (fn) { callback.push (fn); }
+    function runCallbacks () {
+        var result = "\n"
+        var n = 0
+        for (var fn in callback) {
+            n += 1
+            result += "("+String (n)+")"
+            result += callback [fn] ()
+            result += "\n"
+        }
+        return result
+    }
+    function print (text) { console.log (text); }
+"""
+
+proc consoleprint (str:cstring): void {.importc: "print", nodecl.}
+proc print* (a: varargs[string, `$`]) = consoleprint "$1: $2" % [consolePrefix, join(a, " ")]
+
+type CallbackProc {.importc.} = proc () : cstring
+
+proc regCallback (fn:CallbackProc) {.importc.}
+proc runCallbacks ():cstring {.importc.}
+
+proc `*` (s:string, n:Natural) : string = s.repeat(n)
+
+proc outer (i:Natural) : (string, int) =
+    let c = $char(rand(93) + 33)
+    let n = rand(40)
+    let s = c * n
+    proc inner(): cstring = ("[$1]" % $n) & s & " <--"
+    regCallback(inner)
+    return (s, n)
+
+var expected = "\n"
+for i in 1 .. 10:
+    let (s, n) = outer(i)
+    expected &= ("($1)[$2]" % [$i, $n]) & s & " <--"
+    expected &= "\n"
+
+let results = runCallbacks()
+
+doAssert(expected == $results)
+
+block issue7048:
+  block:
+    proc foo(x: seq[int]): auto =
+      proc bar: int = x[1]
+      bar
+
+    var stuff = @[1, 2]
+    let f = foo(stuff)
+    stuff[1] = 321
+    doAssert f() == 2
+
+  block:
+    proc foo(x: tuple[things: string]; y: array[3, int]): auto =
+      proc very: auto = 
+        proc deeply: auto =
+          proc nested: (char, int) = (x.things[0], y[1])
+          nested
+        deeply
+      very()
+
+    var
+      stuff = (things: "NIM")
+      stuff2 = [32, 64, 96]
+    let f = foo(stuff, stuff2)
+    stuff.things = "VIM"
+    stuff2[1] *= 10
+    doAssert f()() == ('N', 64)
+    doAssert (stuff.things[0], stuff2[1]) == ('V', 640)
+
+  block:
+    proc foo(x: ptr string): auto =
+      proc bar(): int = len(x[])
+      bar
+    
+    var 
+      s1 = "xyz"
+      s2 = "stuff"
+      p = addr s1
+    
+    let f = foo(p)
+    p = addr s2
+    doAssert len(p[]) == 5
+    doAssert f() == 3
diff --git a/tests/js/tcodegendeclproc.nim b/tests/js/tcodegendeclproc.nim
new file mode 100644
index 000000000..33064bdf1
--- /dev/null
+++ b/tests/js/tcodegendeclproc.nim
@@ -0,0 +1,11 @@
+discard """
+  output: '''
+-1
+8
+'''
+  ccodecheck: "'console.log(-1); function fac__tcodegendeclproc_u1(n_p0)'"
+"""
+proc fac(n: int): int {.codegenDecl: "console.log(-1); function $2($3)".} =
+  return n
+
+echo fac(8)
diff --git a/tests/js/tcodegendeclvar.nim b/tests/js/tcodegendeclvar.nim
new file mode 100644
index 000000000..645443ef7
--- /dev/null
+++ b/tests/js/tcodegendeclvar.nim
@@ -0,0 +1,10 @@
+discard """
+  output: '''
+-1
+2
+'''
+  ccodecheck: "'console.log(-1); var v_' \\d+ ' = [2]'"
+"""
+
+var v {.codegenDecl: "console.log(-1); var $2".} = 2
+echo v
diff --git a/tests/js/tconsole.nim b/tests/js/tconsole.nim
new file mode 100644
index 000000000..88c71ea18
--- /dev/null
+++ b/tests/js/tconsole.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''
+Hello, console
+1 2 3
+'''
+"""
+
+# This file tests the JavaScript console
+
+import jsconsole
+
+console.log("Hello, console")
+console.log(1, 2, 3)
diff --git a/tests/js/tcopying.nim b/tests/js/tcopying.nim
new file mode 100644
index 000000000..306d25090
--- /dev/null
+++ b/tests/js/tcopying.nim
@@ -0,0 +1,80 @@
+discard """
+  output: '''123
+2 9
+2 9
+1 124
+true false
+100 300 100
+1
+1
+'''
+"""
+
+type MyArray = array[1, int]
+
+proc changeArray(a: var MyArray) =
+    a = [123]
+
+var a: MyArray
+changeArray(a)
+echo a[0]
+
+# bug #4703
+# Test 1
+block:
+    let ary1 = [1, 2, 3]
+    var ary2 = ary1
+
+    ary2[1] = 9
+
+    echo ary1[1], " ", ary2[1]
+
+# Test 2
+block:
+    type TestObj = ref object of RootObj
+        ary2: array[3, int]
+
+    let ary1 = [1, 2, 3]
+    var obj = TestObj(ary2: ary1)
+
+    obj.ary2[1] = 9
+
+    echo ary1[1], " ", obj.ary2[1]
+
+block:
+    type TestObj = object
+        x, y: int
+
+    let obj = TestObj(x: 1, y: 2)
+    var s = @[obj]
+    s[0].x += 123
+    echo obj.x, " ", s[0].x
+
+block:
+    var nums = {1, 2, 3, 4}
+    let obj = (n: nums)
+    nums.incl 5
+    echo (5 in nums), " ", (5 in obj.n)
+
+block:
+    let tup1 = (a: 100)
+    var tup2 = (t: (t2: tup1))
+    var tup3 = tup1
+    tup2.t.t2.a = 300
+    echo tup1.a, " ", tup2.t.t2.a, " ", tup3.a
+
+block:
+    proc foo(arr: array[2, int]) =
+        var s = @arr
+        s[0] = 500
+
+    var nums = [1, 2]
+    foo(nums)
+    echo nums[0]
+
+proc bug9674 =
+  var b = @[1,2,3]
+  var a = move(b)
+  echo a[0]
+
+bug9674()
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/tderef.nim b/tests/js/tderef.nim
new file mode 100644
index 000000000..ddb91bd42
--- /dev/null
+++ b/tests/js/tderef.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''true
+'''
+"""
+
+import tables
+
+type EventStore = Table[string, seq[proc ()]]
+
+proc newEventStore(): EventStore =
+  initTable[string, seq[proc ()]]()
+
+proc register(store: var EventStore, name: string, callback: proc ()) =
+  if not store.hasKey(name):
+    store[name] = @[]
+  store[name].add(callback)
+
+var store = newEventStore()
+store.register("test", proc () = echo "true")
+store["test"][0]()
diff --git a/tests/js/tdiscard.nim b/tests/js/tdiscard.nim
new file mode 100644
index 000000000..9aa6ea1b1
--- /dev/null
+++ b/tests/js/tdiscard.nim
@@ -0,0 +1,3 @@
+import dom
+
+discard Node()
\ No newline at end of file
diff --git a/tests/js/tdollar_float.nim b/tests/js/tdollar_float.nim
new file mode 100644
index 000000000..4fd8e3cba
--- /dev/null
+++ b/tests/js/tdollar_float.nim
@@ -0,0 +1,62 @@
+#[
+merge into tests/system/tdollars.nim once https://github.com/nim-lang/Nim/pull/14122
+is merged
+]#
+
+import unittest
+
+block: # https://github.com/timotheecour/Nim/issues/133
+  # simple test
+  var a: float = 2
+  check $a == "2.0"
+
+  # systematic tests
+  template fun(a2: static float) =
+    const a: float = a2 # needed pending https://github.com/timotheecour/Nim/issues/132
+    var b = a
+    check $b == $a
+
+  fun 2
+  fun 2.0
+  fun 2.1
+  fun 1_000
+  fun 1_000.1
+  fun 1_000_000_000.1
+  fun 1_000_000_000_000.1
+
+  # negatives
+  fun -2.0
+  fun -2.1
+
+  # 0
+  fun 0
+  fun -0
+  fun 0.0
+
+  block:
+    var a = -0.0
+    check $a in ["-0.0", "0.0"]
+
+  # exponents
+  block:
+    var a = 5e20
+    check $a in ["5e20", "500000000000000000000.0"]
+
+  fun 3.4e1'f32
+  fun 3.4e-1'f32
+  fun -3.4e-1'f32
+  fun 3.4e-1'f32
+  fun 3e-1'f32
+
+  block:
+    var a = 3.4e38'f32
+    check $a in ["3.4e+38", "3.4e+038"]
+      # on windows, printf (used in VM) prints as 3.4e+038
+      # but js prints as 3.4e+38
+      # on osx, both print as 3.4e+38
+      # see https://github.com/timotheecour/Nim/issues/138
+
+  when false: # edge cases
+    fun -0.0 # see https://github.com/timotheecour/Nim/issues/136
+    fun 5e20
+    fun 3.4e38'f32
diff --git a/tests/js/temptyseq.nim b/tests/js/temptyseq.nim
new file mode 100644
index 000000000..6489cf817
--- /dev/null
+++ b/tests/js/temptyseq.nim
@@ -0,0 +1,8 @@
+# #12671
+
+proc foo =
+  var x: seq[int]
+  doAssertRaises(IndexDefect):
+    inc x[0]
+
+foo()
diff --git a/tests/js/tenumhole.nim b/tests/js/tenumhole.nim
new file mode 100644
index 000000000..71a493e8c
--- /dev/null
+++ b/tests/js/tenumhole.nim
@@ -0,0 +1,12 @@
+discard """
+  output: "first0second32third64"
+"""
+
+type Holed = enum
+  hFirst = (0,"first")
+  hSecond = (32,"second")
+  hThird = (64,"third")
+  
+var x = @[0,32,64] # This is just to avoid the compiler inlining the value of the enum
+
+echo Holed(x[0]),ord Holed(x[0]),Holed(x[1]),ord Holed(x[1]),Holed(x[2]),ord Holed(x[2])
diff --git a/tests/js/tenumnegkey.nim b/tests/js/tenumnegkey.nim
new file mode 100644
index 000000000..f96c554d4
--- /dev/null
+++ b/tests/js/tenumnegkey.nim
@@ -0,0 +1,12 @@
+discard """
+  output: "first-12second32third64"
+"""
+
+type Holed = enum
+  hFirst = (-12,"first")
+  hSecond = (32,"second")
+  hThird = (64,"third")
+  
+var x = @[-12,32,64] # This is just to avoid the compiler inlining the value of the enum
+
+echo Holed(x[0]),ord Holed(x[0]),Holed(x[1]),ord Holed(x[1]),Holed(x[2]),ord Holed(x[2])
diff --git a/tests/js/tenumoffset.nim b/tests/js/tenumoffset.nim
new file mode 100644
index 000000000..5bdc4c105
--- /dev/null
+++ b/tests/js/tenumoffset.nim
@@ -0,0 +1,19 @@
+discard """
+  output: "my value A1my value Bconc2valueCabc4abc"
+"""
+
+const
+  strValB = "my value B"
+
+type
+  TMyEnum = enum
+    valueA = (1, "my value A"),
+    valueB = strValB & "conc",
+    valueC,
+    valueD = (4, "abc")
+
+proc getValue(i:int): TMyEnum = TMyEnum(i)
+
+# trick the optimizer with a variable:
+var x = getValue(4)
+echo getValue(1), ord(valueA), getValue(2), ord(valueB), getValue(3), getValue(4), ord(valueD), x
diff --git a/tests/js/test1.nim b/tests/js/test1.nim
new file mode 100644
index 000000000..7ad3f85f6
--- /dev/null
+++ b/tests/js/test1.nim
@@ -0,0 +1,52 @@
+discard """
+  output: "1261129"
+"""
+
+# This file tests the JavaScript generator
+
+import strutils
+
+var
+  inputElement = "1123"
+
+proc onButtonClick(inputElement: string) {.exportc.} =
+  let v = $inputElement
+  if v.allCharsInSet(WhiteSpace):
+    echo "only whitespace, hu?"
+  else:
+    var x = parseInt(v)
+    echo x*x
+
+onButtonClick(inputElement)
+
+block:
+  var s: string
+  s.add("hi")
+  doAssert(s == "hi")
+
+block:
+  var s: string
+  s.insert("hi", 0)
+  doAssert(s == "hi")
+
+block:
+  var s: string
+  s.setLen(2)
+  s[0] = 'h'
+  s[1] = 'i'
+  doAssert(s == "hi")
+
+block:
+  var s: seq[int]
+  s.setLen(2)
+  doAssert(s == @[0, 0])
+
+block:
+  var s: seq[int]
+  s.insert(2, 0)
+  doAssert(s == @[2])
+
+block:
+  var s: seq[int]
+  s.add(2)
+  doAssert(s == @[2])
diff --git a/tests/js/test2.nim b/tests/js/test2.nim
new file mode 100644
index 000000000..fa857ccc5
--- /dev/null
+++ b/tests/js/test2.nim
@@ -0,0 +1,58 @@
+discard """
+  output: '''foo
+js 3.14
+7
+1
+-21550
+-21550'''
+"""
+
+# This file tests the JavaScript generator
+
+doAssert getCurrentException() == nil
+doAssert getCurrentExceptionMsg() == ""
+
+#  #335
+proc foo() =
+  var bar = "foo"
+  proc baz() =
+    echo bar
+  baz()
+foo()
+
+# #376
+when not defined(js):
+  proc foo(val: float): string = "no js " & $val
+else:
+  proc foo(val: float): string = "js " & $val
+
+echo foo(3.14)
+
+# #2495
+type C = concept x
+
+proc test(x: C, T: typedesc): T =
+  cast[T](x)
+
+echo 7.test(int8)
+
+# #4222
+const someConst = [ "1"]
+
+proc procThatRefersToConst() # Forward decl
+procThatRefersToConst() # Call bar before it is defined
+
+proc procThatRefersToConst() =
+  var i = 0 # Use a var index, otherwise nim will constfold foo[0]
+  echo someConst[i] # JS exception here: foo is still not initialized (undefined)
+
+# bug #6753
+let x = -1861876800
+const y = 86400
+echo (x - (y - 1)) div y # Now gives `-21550`
+
+proc foo09() =
+    let x = -1861876800
+    const y = 86400
+    echo (x - (y - 1)) div y # Still gives `-21551`
+foo09()
diff --git a/tests/js/testmagic.nim b/tests/js/testmagic.nim
new file mode 100644
index 000000000..8e06f1a9b
--- /dev/null
+++ b/tests/js/testmagic.nim
@@ -0,0 +1,12 @@
+discard """
+  output: '''true
+123
+'''
+"""
+
+# This file tests some magic
+
+var foo = cstring("foo")
+var bar = cstring("foo")
+echo(foo == bar)
+echo "01234"[1 .. ^2]
diff --git a/tests/js/testobjs.nim b/tests/js/testobjs.nim
new file mode 100644
index 000000000..b61d06471
--- /dev/null
+++ b/tests/js/testobjs.nim
@@ -0,0 +1,73 @@
+discard """
+  output: '''{"columns":[{"t":null},{"t":null}]}
+{"columns":[{"t":null},{"t":null}]}
+'''
+"""
+
+## Tests javascript object generation
+
+type
+  Kg = distinct float
+  Price = int
+  Item = object of RootObj
+    weight: Kg
+    price: Price
+    desc: cstring
+  Person = object of RootObj
+    name: cstring
+    age: int
+    item: Item
+  Test = object
+    name: cstring
+  Recurse[T] = object
+    data: T
+    next: ref Recurse[T]
+
+var
+  test = Test(name: "Jorden")
+  sword = Item(desc: "pointy", weight: Kg(10.0),
+                price: Price(50))
+  knight = Person(name: "robert", age: 19, item: sword)
+  recurse4 = (ref Recurse[int])(data: 4, next: nil)
+  recurse3 = (ref Recurse[int])(data: 3, next: recurse4)
+  recurse2 = (ref Recurse[int])(data: 2, next: recurse3)
+  recurse1 = Recurse[int](data: 1, next: recurse2)
+
+
+doAssert test.name == cstring"Jorden"
+doAssert knight.age == 19
+doAssert knight.item.price == 50
+doAssert recurse1.next.next.data == 3
+
+# bug #6035
+proc toJson*[T](data: T): cstring {.importc: "JSON.stringify".}
+
+type
+  Column = object
+    t: ref Column
+
+  Test2 = object
+    columns: seq[Column]
+
+var test1 = Test2(columns: @[Column(t: nil), Column(t: nil)])
+let test2 = test1
+
+echo toJSON(test1)
+echo toJSON(test2)
+
+block issue10005:
+  type
+    Player = ref object of RootObj
+      id*: string
+      nickname*: string
+      color*: string
+
+  proc newPlayer(nickname: string, color: string): Player =
+    let pl = Player(color: "#123", nickname: nickname)
+    return Player(
+        id: "foo",
+        nickname: nickname,
+        color: color,
+    )
+
+  doAssert newPlayer("foo", "#1232").nickname == "foo"
diff --git a/tests/js/testtojsstr.nim b/tests/js/testtojsstr.nim
new file mode 100644
index 000000000..03ac89e20
--- /dev/null
+++ b/tests/js/testtojsstr.nim
@@ -0,0 +1,8 @@
+discard """
+  output = "И\n"
+"""
+
+let s: string = "И\n"
+let cs = s.cstring
+
+echo $s
diff --git a/tests/js/tfieldchecks.nim b/tests/js/tfieldchecks.nim
new file mode 100644
index 000000000..a0679a349
--- /dev/null
+++ b/tests/js/tfieldchecks.nim
@@ -0,0 +1,48 @@
+discard """
+  output: '''
+foo
+C
+3.14
+foo
+3.14
+3.14
+'''
+"""
+
+type
+  V = enum
+    A, B, C
+  X = object
+    f0: string
+    case f1: V
+    of A: f2: string
+    of B: discard
+    of C: f3: float
+
+var obj = X(f0: "foo", f1: C, f3: 3.14)
+
+block:
+  echo obj.f0
+  echo obj.f1
+  doAssertRaises(FieldDefect): echo obj.f2
+  echo obj.f3
+
+block:
+  let a0 = addr(obj.f0)
+  echo a0[]
+  # let a1 = addr(obj.f1)
+  # echo a1[]
+  doAssertRaises(FieldDefect):
+    let a2 = addr(obj.f2)
+    echo a2[]
+  let a3 = addr(obj.f3)
+  echo a3[]
+
+# Prevent double evaluation of LHS
+block:
+  var flag = false
+  proc wrap(x: X): X =
+    doAssert flag == false
+    flag = true
+    result = x
+  echo wrap(obj).f3
diff --git a/tests/js/tfloatround.nim b/tests/js/tfloatround.nim
new file mode 100644
index 000000000..7bc5430e6
--- /dev/null
+++ b/tests/js/tfloatround.nim
@@ -0,0 +1,7 @@
+discard """
+  output: '''
+3
+'''
+"""
+
+echo int(22 / 7)
diff --git a/tests/js/tglobal.nim b/tests/js/tglobal.nim
new file mode 100644
index 000000000..38f5eec34
--- /dev/null
+++ b/tests/js/tglobal.nim
@@ -0,0 +1,30 @@
+block global:
+  proc getState(): int =
+    var state0 {.global.}: int
+    inc state0
+    result = state0
+
+  for i in 0 ..< 3:
+    doAssert getState() == i + 1
+
+  for i in 0 ..< 3:
+    once:
+      doAssert i == 0
+
+
+block threadvar:
+  proc getThreadState0(): int =
+    var state0 {.threadvar.}: int
+    inc state0
+    result = state0
+
+  for i in 0 ..< 3:
+    doAssert getThreadState0() == i + 1
+
+  proc getThreadState1(): int =
+    var state1 {.threadvar.}: int
+    inc state1
+    result = state1
+
+  for i in 0 ..< 3:
+    doAssert getThreadState1() == i + 1
diff --git a/tests/js/timplicit_nodecl.nim b/tests/js/timplicit_nodecl.nim
new file mode 100644
index 000000000..79a921815
--- /dev/null
+++ b/tests/js/timplicit_nodecl.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''22
+22'''
+"""
+
+# test implicit nodecl
+block:
+  {. emit: "var importMe = 22;" .}
+  var
+    a {. importc: "importMe" .}: int
+    importMe {. importc .}: int
+  echo a
+  echo importMe
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
new file mode 100644
index 000000000..f27ea5546
--- /dev/null
+++ b/tests/js/tjsffi.nim
@@ -0,0 +1,274 @@
+discard """
+matrix: "--legacy:jsnolambdalifting;"
+output: '''
+3
+2
+12
+Event { name: 'click: test' }
+Event { name: 'reloaded: test' }
+Event { name: 'updates: test' }
+'''
+"""
+
+import jsffi, jsconsole
+
+# Tests for JsObject
+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: # 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
+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: # 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
+block: # Test lit
+  type TestObject = object
+    a: int
+    b: cstring
+  {. emit: "var comparison = {a: 1};" .}
+  var comparison {. importjs, nodecl .}: TestObject
+  let obj = TestObject{ a: 1 }
+  doAssert obj == comparison
+
+block: # Test bindMethod
+  type TestObject = object
+    a: int
+    onWhatever: proc(e: int): int {.nimcall.}
+  proc handleWhatever(this: TestObject, e: int): int =
+    e + this.a
+  block:
+    let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
+    doAssert obj.onWhatever(1) == 10
+
+block:
+  {.emit: "function jsProc(n) { return n; }" .}
+  proc jsProc(x: int32): JsObject {.importjs: "jsProc(#)".}
+  block:
+    var x = jsProc(1)
+    var y = jsProc(2)
+    console.log x + y
+    console.log ++x
+
+    x += jsProc(10)
+    console.log x
+
+block:
+  {.emit:
+  """
+  function Event(name) { this.name = name; }
+  function on(eventName, eventHandler) { eventHandler(new Event(eventName + ": test")); }
+  var jslib = { "on": on, "subscribe": on };
+  """
+  .}
+
+  type Event = object
+    name: cstring
+
+  proc on(event: cstring, handler: proc) {.importjs: "on(#,#)".}
+  var jslib {.importjs: "jslib", nodecl.}: JsObject
+
+  on("click") do (e: Event):
+    console.log e
+
+  jslib.on("reloaded") do ():
+    console.log jsarguments[0]
+
+  # this test case is different from the above, because
+  # `subscribe` is not overloaded in the current scope
+  jslib.subscribe("updates"):
+    console.log jsarguments[0]
+
+block:
+  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)
+  var b = toJs(0)
+  doAssert to(a ** b, int) == 1
+  a = toJs(1)
+  b = toJs(1)
+  doAssert to(a ** b, int) == 1
+  a = toJs(-1)
+  b = toJs(-1)
+  doAssert to(a ** b, int) == -1
+  a = toJs(6)
+  b = toJs(6)
+  doAssert to(a ** b, int) == 46656
+  a = toJs(5.5)
+  b = toJs(3)
+  doAssert to(a ** b, float) == 166.375
+  a = toJs(5)
+  b = toJs(3.0)
+  doAssert to(a ** b, float) == 125.0
+  a = toJs(7.0)
+  b = toJS(6.0)
+  doAssert to(a ** b, float) == 117649.0
+  a = toJs(8)
+  b = toJS(-2)
+  doAssert to(a ** b, float) == 0.015625
+
+  a = toJs(1)
+  b = toJs(1)
+  doAssert to(`**`(a + a, b), int) == 2
+
+  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
new file mode 100644
index 000000000..378003f4e
--- /dev/null
+++ b/tests/js/tjsffi_old.nim
@@ -0,0 +1,340 @@
+discard """
+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
+'''
+"""
+
+## 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
+# 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()
+
+# 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 {. importc, 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 {. importc, nodecl .}: JsObject
+    let obj = JsObject{ a: 22, b: "test".cstring }
+    obj.a == comparison.a and obj.b == comparison.b
+  echo test()
+
+# 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()
+
+# 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 {. importcpp, 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 {. importcpp, 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()
+
+# Tests for macros on non-JsRoot objects
+# Test lit
+block:
+  type TestObject = object
+    a: int
+    b: cstring
+  proc test(): bool =
+    {. emit: "var comparison = {a: 1};" .}
+    var comparison {. importc, nodecl .}: TestObject
+    let obj = TestObject{ a: 1 }
+    obj == comparison
+  echo test()
+
+# Test bindMethod
+block:
+  type TestObject = object
+    a: 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))
+    obj.onWhatever(1) == 10
+  echo test()
+
+block:
+  {.emit: "function jsProc(n) { return n; }" .}
+  proc jsProc(x: int32): JsObject {.importc: "jsProc".}
+
+  proc test() =
+    var x = jsProc(1)
+    var y = jsProc(2)
+    console.log x + y
+    console.log ++x
+
+    x += jsProc(10)
+    console.log x
+
+  test()
+
+
+block:
+  {.emit:
+  """
+  function Event(name) { this.name = name; }
+  function on(eventName, eventHandler) { eventHandler(new Event(eventName + ": test")); }
+  var jslib = { "on": on, "subscribe": on };
+  """
+  .}
+
+  type Event = object
+    name: cstring
+
+  proc on(event: cstring, handler: proc) {.importc: "on".}
+  var jslib {.importc: "jslib", nodecl.}: JsObject
+
+  on("click") do (e: Event):
+    console.log e
+
+  jslib.on("reloaded") do ():
+    console.log jsarguments[0]
+
+  # this test case is different from the above, because
+  # `subscribe` is not overloaded in the current scope
+  jslib.subscribe("updates"):
+    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
diff --git a/tests/js/tjshello.nim b/tests/js/tjshello.nim
new file mode 100644
index 000000000..8e090b3d2
--- /dev/null
+++ b/tests/js/tjshello.nim
@@ -0,0 +1,10 @@
+discard """
+  cmd: "nim $target $options --stackTrace:off --lineTrace:off $file"
+  output: "Hello World"
+  maxcodesize: 1000
+  ccodecheck: "!@'function'"
+"""
+
+import jsconsole
+
+console.log "Hello World"
diff --git a/tests/js/tjshello_stacktrace.nim b/tests/js/tjshello_stacktrace.nim
new file mode 100644
index 000000000..d5e1c36eb
--- /dev/null
+++ b/tests/js/tjshello_stacktrace.nim
@@ -0,0 +1,9 @@
+discard """
+  output: "Hello World"
+  maxcodesize: 4500
+  ccodecheck: "!@'function'"
+"""
+
+import jsconsole
+
+console.log "Hello World"
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/tmangle.nim b/tests/js/tmangle.nim
new file mode 100644
index 000000000..caaa15fa1
--- /dev/null
+++ b/tests/js/tmangle.nim
@@ -0,0 +1,106 @@
+discard """
+  output: '''true
+true
+true
+true
+true
+true
+true'''
+"""
+
+# Test not mangled:
+block:
+  type T = object
+    a: int
+    b: cstring
+  proc test(): bool =
+    let obj = T(a: 11, b: "foo")
+    {. emit: [result, " = (", obj, ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", obj, ".b == \"foo\");"] .}
+  echo test()
+
+# Test indirect (fields in genAddr):
+block:
+  type T = object
+    a: int
+    b: cstring
+  var global = T(a: 11, b: "foo")
+  proc test(): bool =
+    var obj = T(a: 11, b: "foo")
+    {. emit: [result, " = (", obj.addr[], ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", obj.addr[], ".b == \"foo\");"] .}
+    {. emit: [result, " = ", result, " && (", global, ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", global, ".b == \"foo\");"] .}
+  echo test()
+
+# Test addr of field:
+block:
+  type T = object
+    a: int
+    b: cstring
+  proc test(): bool =
+    var obj = T(a: 11, b: "foo")
+    result = obj.a.addr[] == 11
+    result = result and obj.b.addr[] == "foo".cstring
+  echo test()
+
+# Test reserved words:
+block:
+  type T = ref object
+    `if`: int
+    `for`: int
+    `==`: cstring
+    `&&`: cstring
+  proc test(): bool =
+    var
+      obj1 = T(`if`: 11, `for`: 22, `==`: "foo", `&&`: "bar")
+      obj2: T
+    new obj2 # Test behaviour for createRecordVarAux.
+    result = obj1.`if` == 11
+    result = result and obj1.addr[].`for` == 22
+    result = result and obj1.`==` == "foo".cstring
+    result = result and obj1.`&&`.addr[] == "bar".cstring
+    result = result and obj2.`if` == 0
+    result = result and obj2.`for` == 0
+    result = result and obj2.`==`.isNil
+    result = result and obj2.`&&`.isNil
+  echo test()
+
+# Test codegen for fields with uppercase letters:
+block:
+  type MyObj = object
+    mField: int
+  proc test(): bool =
+    var a: MyObj
+    var b = a
+    result = b.mField == 0
+  echo test()
+
+# Test tuples
+block:
+  type T = tuple
+    a: int
+    b: int
+  proc test(): bool =
+    var a: T = (a: 1, b: 1)
+    result = a.a == 1
+    result = result and a.b == 1
+  echo test()
+
+# Test importc / exportc fields:
+block:
+  type T = object
+    a: int
+    b {. importc: "notB" .}: cstring
+  type U = object
+    a: int
+    b {. exportc: "notB" .}: cstring
+  proc test(): bool =
+    var
+      obj1 = T(a: 11, b: "foo")
+      obj2 = U(a: 11, b: "foo")
+    {. emit: [result, " = (", obj1, ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", obj1, ".notB == \"foo\");"] .}
+    {. emit: [result, " = (", obj2, ".a == 11);"] .}
+    {. emit: [result, " = ", result, " && (", obj2, ".notB == \"foo\");"] .}
+  echo test()
diff --git a/tests/js/tmodify_cstring.nim b/tests/js/tmodify_cstring.nim
new file mode 100644
index 000000000..82f8ccb23
--- /dev/null
+++ b/tests/js/tmodify_cstring.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "cstring doesn't support `[]=` operator"
+"""
+
+var x = cstring"abcd"
+x[0] = 'x'
diff --git a/tests/js/tnativeexc.nim b/tests/js/tnativeexc.nim
new file mode 100644
index 000000000..8b2b43e8f
--- /dev/null
+++ b/tests/js/tnativeexc.nim
@@ -0,0 +1,31 @@
+discard """
+  action: "run"
+"""
+
+import jsffi
+
+# Can catch JS exceptions
+try:
+  asm """throw new Error('a new error');"""
+except JsError as e:
+  doAssert e.message == "a new error"
+except:
+  doAssert false
+
+# Can distinguish different exceptions
+try:
+  asm """JSON.parse(';;');"""
+except JsEvalError:
+  doAssert false
+except JsSyntaxError as se:
+  doAssert se.message == "Unexpected token ';', \";;\" is not valid JSON"
+except JsError as e:
+  doAssert false
+
+# Can catch parent exception
+try:
+  asm """throw new SyntaxError();"""
+except JsError as e:
+  discard
+except:
+  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
new file mode 100644
index 000000000..6c1e4e401
--- /dev/null
+++ b/tests/js/tnilstrs.nim
@@ -0,0 +1,25 @@
+block:
+  var x: string
+  var y = "foo"
+
+  echo x
+  doAssert x == ""
+  doAssert "" == x
+
+  add(x, y)
+  y[0] = 'm'
+  doAssert y == "moo" and x == "foo"
+
+block:
+  var x = "foo".cstring
+  var y: string
+  add(y, x)
+  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/tobjfieldbyvar.nim b/tests/js/tobjfieldbyvar.nim
new file mode 100644
index 000000000..91a3c1315
--- /dev/null
+++ b/tests/js/tobjfieldbyvar.nim
@@ -0,0 +1,20 @@
+discard """
+  output: '''5
+'''
+"""
+
+# bug #2798
+
+type Inner = object
+  value: int
+
+type Outer = object
+  i: Inner
+
+proc test(i: var Inner) =
+  i.value += 5
+
+var o: Outer
+test(o.i)
+
+echo o.i.value
diff --git a/tests/js/tos.nim b/tests/js/tos.nim
new file mode 100644
index 000000000..40fb52bcf
--- /dev/null
+++ b/tests/js/tos.nim
@@ -0,0 +1,21 @@
+# xxx consider merging this in tests/stdlib/tos.nim for increased coverage (with selecting disabling)
+
+static: doAssert defined(nodejs)
+
+import os
+
+block:
+  doAssert "./foo//./bar/".normalizedPath == "foo/bar"
+  doAssert relativePath(".//foo/bar", "foo") == "bar"
+  doAssert "/".isAbsolute
+  doAssert not "".isAbsolute
+  doAssert not ".".isAbsolute
+  doAssert not "foo".isAbsolute
+  doAssert relativePath("", "bar") == ""
+  doAssert normalizedPath(".///foo//./") == "foo"
+
+  when nimvm: discard
+  else:
+    let cwd = getCurrentDir()
+    doAssert cwd.isAbsolute
+    doAssert relativePath(getCurrentDir() / "foo", "bar") == ".." / "foo"
diff --git a/tests/js/trefbyvar.nim b/tests/js/trefbyvar.nim
new file mode 100644
index 000000000..5b168044e
--- /dev/null
+++ b/tests/js/trefbyvar.nim
@@ -0,0 +1,69 @@
+discard """
+  output: '''0
+5
+0
+5
+@[1, 2]
+~'''
+"""
+
+# bug #2476
+
+type A = ref object
+    m: int
+
+proc f(a: var A) =
+    var b: A
+    b.new()
+    b.m = 5
+    a = b
+
+var t: A
+t.new()
+
+echo t.m
+t.f()
+echo t.m
+
+proc main =
+  # now test the same for locals
+  var t: A
+  t.new()
+
+  echo t.m
+  t.f()
+  echo t.m
+
+main()
+
+# bug #5974
+type
+  View* = object
+    data: ref seq[int]
+
+let a = View(data: new(seq[int]))
+a.data[] = @[1, 2]
+
+echo a.data[]
+
+# bug #5379
+var input = newSeq[ref string]()
+input.add(nil)
+input.add(new string)
+input[1][] = "~"
+echo input[1][]
+
+# bug #5517
+type
+  TypeA1 = object of RootObj
+    a_impl: int
+    b_impl: string
+    c_impl: pointer
+
+proc initTypeA1(a: int; b: string; c: pointer = nil): TypeA1 =
+  result.a_impl = a
+  result.b_impl = b
+  result.c_impl = c
+
+let x = initTypeA1(1, "a")
+doAssert($x == "(a_impl: 1, b_impl: \"a\", c_impl: ...)")
diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim
new file mode 100644
index 000000000..a562ad63b
--- /dev/null
+++ b/tests/js/trepr.nim
@@ -0,0 +1,413 @@
+# xxx consider merging with `tests/stdlib/trepr.nim` to increase overall test coverage
+
+block ints:
+  let
+    na: int8 = -120'i8
+    nb: int16 = -32700'i16
+    nc: int32 = -2147483000'i32
+    nd: int64 = -9223372036854775000'i64
+    ne: int = -1234567
+    pa: int8 = 120'i8
+    pb: int16 = 32700'i16
+    pc: int32 = 2147483000'i32
+    pd: int64 = 9223372036854775000'i64
+    pe: int = 1234567
+
+  doAssert(repr(na) == "-120")
+  doAssert(repr(nb) == "-32700")
+  doAssert(repr(nc) == "-2147483000")
+  doAssert(repr(nd) == "-9223372036854775000")
+  doAssert(repr(ne) == "-1234567")
+  doAssert(repr(pa) == "120")
+  doAssert(repr(pb) == "32700")
+  doAssert(repr(pc) == "2147483000")
+  doAssert(repr(pd) == "9223372036854775000")
+  doAssert(repr(pe) == "1234567")
+
+block uints:
+  let
+    a: uint8 = 254'u8
+    b: uint16 = 65300'u16
+    c: uint32 = 4294967290'u32
+    # d: uint64 = 18446744073709551610'u64  -> unknown node type
+    e: uint = 1234567
+
+  doAssert(repr(a) == "254")
+  doAssert(repr(b) == "65300")
+  doAssert(repr(c) == "4294967290")
+  # doAssert(repr(d) == "18446744073709551610")
+  doAssert(repr(e) == "1234567")
+
+block floats:
+  let
+    a: float32 = 3.4e38'f32
+    b: float64 = 1.7976931348623157e308'f64
+    c: float = 1234.567e89
+
+  when defined js:
+    doAssert(repr(a) == "3.4e+38") # in C: 3.399999952144364e+038
+    doAssert(repr(b) == "1.7976931348623157e+308") # in C: 1.797693134862316e+308
+    doAssert(repr(c) == "1.234567e+92") # in C: 1.234567e+092
+
+block bools:
+  let
+    a: bool = true
+    b: bool = false
+
+  doAssert(repr(a) == "true")
+  doAssert(repr(b) == "false")
+
+block enums:
+  type
+    AnEnum = enum
+      aeA
+      aeB
+      aeC
+    HoledEnum = enum
+      heA = -12
+      heB = 15
+      heC = 123
+
+  doAssert(repr(aeA) == "aeA")
+  doAssert(repr(aeB) == "aeB")
+  doAssert(repr(aeC) == "aeC")
+  doAssert(repr(heA) == "heA")
+  doAssert(repr(heB) == "heB")
+  doAssert(repr(heC) == "heC")
+
+block emums_and_unicode: #6741
+  type K = enum Kanji = "漢字"
+  let kanji = Kanji
+  doAssert(kanji == Kanji, "Enum values are not equal")
+  doAssert($kanji == $Kanji, "Enum string values are not equal")
+
+block chars:
+  let
+    a = 'a'
+    b = 'z'
+    one = '1'
+    nl = '\x0A'
+
+  doAssert(repr(a) == "'a'")
+  doAssert(repr(b) == "'z'")
+  doAssert(repr(one) == "'1'")
+  doAssert(repr(nl) == "'\\10'")
+
+block strings:
+  let
+    a: string = "12345"
+    b: string = "hello,repr"
+    c: string = "hi\nthere"
+  when defined js: # C prepends the pointer, JS does not.
+    doAssert(repr(a) == "\"12345\"")
+    doAssert(repr(b) == "\"hello,repr\"")
+    doAssert(repr(c) == "\"hi\\10there\"")
+
+block sets:
+  let
+    a: set[int16] = {1'i16, 2'i16, 3'i16}
+    b: set[char] = {'A', 'k'}
+
+  doAssert(repr(a) == "{1, 2, 3}")
+  doAssert(repr(b) == "{'A', 'k'}")
+
+block ranges:
+  let
+    a: range[0..12] = 6
+    b: range[-12..0] = -6
+  doAssert(repr(a) == "6")
+  doAssert(repr(b) == "-6")
+
+block tuples:
+  type
+    ATuple = tuple
+      a: int
+      b: float
+      c: string
+      d: OtherTuple
+    OtherTuple = tuple
+      a: bool
+      b: int8
+
+  let
+    ot: OtherTuple = (a: true, b: 120'i8)
+    t: ATuple = (a: 42, b: 12.34, c: "tuple", d: ot)
+  when defined js:
+    doAssert(repr(ot) == """
+[Field0 = true,
+Field1 = 120]""")
+    doAssert(repr(t) == """
+[Field0 = 42,
+Field1 = 12.34,
+Field2 = "tuple",
+Field3 = [Field0 = true,
+Field1 = 120]]""")
+
+block objects:
+  type
+    AnObj = object
+      a: int
+      b: float
+      c: OtherObj
+    OtherObj = object
+      a: bool
+      b: int8
+  let
+    oo: OtherObj = OtherObj(a: true, b: 120'i8)
+    o: AnObj = AnObj(a: 42, b: 12.34, c: oo)
+
+  doAssert(repr(oo) == """
+[a = true,
+b = 120]""")
+  doAssert(repr(o) == """
+[a = 42,
+b = 12.34,
+c = [a = true,
+b = 120]]""")
+
+block arrays:
+  type
+    AObj = object
+      x: int
+      y: array[3,float]
+  let
+    a = [0.0, 1, 2]
+    b = [a, a, a]
+    o = AObj(x: 42, y: a)
+    c = [o, o, o]
+    d = ["hi", "array", "!"]
+
+  doAssert(repr(a) == "[0.0, 1.0, 2.0]")
+  doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]")
+  doAssert(repr(c) == """
+[[x = 42,
+y = [0.0, 1.0, 2.0]], [x = 42,
+y = [0.0, 1.0, 2.0]], [x = 42,
+y = [0.0, 1.0, 2.0]]]""")
+  doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]")
+
+block seqs:
+  type
+    AObj = object
+      x: int
+      y: seq[float]
+  let
+    a = @[0.0, 1, 2]
+    b = @[a, a, a]
+    o = AObj(x: 42, y: a)
+    c = @[o, o, o]
+    d = @["hi", "array", "!"]
+
+  doAssert(repr(a) == "@[0.0, 1.0, 2.0]")
+  doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]")
+  doAssert(repr(c) == """
+@[[x = 42,
+y = @[0.0, 1.0, 2.0]], [x = 42,
+y = @[0.0, 1.0, 2.0]], [x = 42,
+y = @[0.0, 1.0, 2.0]]]""")
+  doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]")
+
+block ptrs:
+  type
+    AObj = object
+      x: ptr array[2, AObj]
+      y: int
+  var
+    a = [12.0, 13.0, 14.0]
+    b = addr a[0]
+    c = addr a[2]
+    d = AObj()
+
+  doAssert(repr(a) == "[12.0, 13.0, 14.0]")
+  doAssert(repr(b) == "ref 0 --> 12.0")
+  doAssert(repr(c) == "ref 2 --> 14.0")
+  doAssert(repr(d) == """
+[x = nil,
+y = 0]""")
+
+block ptrs:
+  type
+    AObj = object
+      x: ref array[2, AObj]
+      y: int
+  var
+    a = AObj()
+
+  new(a.x)
+
+  doAssert(repr(a) == """
+[x = ref 0 --> [[x = nil,
+y = 0], [x = nil,
+y = 0]],
+y = 0]""")
+
+block procs:
+  proc test(): int =
+    echo "hello"
+  var
+    ptest = test
+    nilproc: proc(): int
+
+  doAssert(repr(test) == "0")
+  doAssert(repr(ptest) == "0")
+  doAssert(repr(nilproc) == "nil")
+
+block bunch:
+  type
+    AnEnum = enum
+      eA, eB, eC
+    B = object
+      a: string
+      b: seq[char]
+    A = object
+      a: uint32
+      b: int
+      c: float
+      d: char
+      e: AnEnum
+      f: string
+      g: set[char]
+      h: set[int16]
+      i: array[3,string]
+      j: seq[string]
+      k: range[-12..12]
+      l: B
+      m: ref B
+      n: ptr B
+      o: tuple[x: B, y: string]
+      p: proc(b: B): ref B
+      q: cstring
+
+  proc refB(b:B):ref B =
+    new result
+    result[] = b
+
+  var
+    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},
+                i: ["hello", "world", "array"],
+                j: @["hello", "world", "seq"], k: -1,
+                l: bb, m: refB(bb), n: addr bb,
+                o: (bb, "tuple!"), p: refB, q: "cstringtest" )
+
+  doAssert(repr(aa) == """
+[a = 0,
+b = 0,
+c = 0.0,
+d = '\0',
+e = eA,
+f = "",
+g = {},
+h = {},
+i = ["", "", ""],
+j = @[],
+k = -12,
+l = [a = "",
+b = @[]],
+m = nil,
+n = nil,
+o = [Field0 = [a = "",
+b = @[]],
+Field1 = ""],
+p = nil,
+q = nil]""")
+  doAssert(repr(cc) == """
+[a = 12,
+b = 1,
+c = 1.2,
+d = '\0',
+e = eC,
+f = "hello",
+g = {'A'},
+h = {2},
+i = ["hello", "world", "array"],
+j = @["hello", "world", "seq"],
+k = -1,
+l = [a = "inner",
+b = @['o', 'b', 'j']],
+m = ref 0 --> [a = "inner",
+b = @['o', 'b', 'j']],
+n = ref 0 --> [a = "inner",
+b = @['o', 'b', 'j']],
+o = [Field0 = [a = "inner",
+b = @['o', 'b', 'j']],
+Field1 = "tuple!"],
+p = 0,
+q = "cstringtest"]""")
+
+block another:
+  type
+    Size1 = enum
+      s1a, s1b
+    Size2 = enum
+      s2c=0, s2d=20000
+    Size3 = enum
+      s3e=0, s3f=2000000000
+
+  doAssert(repr([s1a, s1b]) == "[s1a, s1b]")
+  doAssert(repr([s2c, s2d]) == "[s2c, s2d]")
+  doAssert(repr([s3e, s3f]) == "[s3e, s3f]")
+
+block another2:
+
+  type
+    AnEnum = enum
+      en1, en2, en3, en4, en5, en6
+
+    Point {.final.} = object
+      x, y, z: int
+      s: array[0..1, string]
+      e: AnEnum
+
+  var
+    p: Point
+    q: ref Point
+    s: seq[ref Point]
+
+  p.x = 0
+  p.y = 13
+  p.z = 45
+  p.s[0] = "abc"
+  p.s[1] = "xyz"
+  p.e = en6
+
+  new(q)
+  q[] = p
+
+  s = @[q, q, q, q]
+
+  doAssert(repr(p) == """
+[x = 0,
+y = 13,
+z = 45,
+s = ["abc", "xyz"],
+e = en6]""")
+  doAssert(repr(q) == """
+ref 0 --> [x = 0,
+y = 13,
+z = 45,
+s = ["abc", "xyz"],
+e = en6]""")
+  doAssert(repr(s) == """
+@[ref 0 --> [x = 0,
+y = 13,
+z = 45,
+s = ["abc", "xyz"],
+e = en6], ref 1 --> [x = 0,
+y = 13,
+z = 45,
+s = ["abc", "xyz"],
+e = en6], ref 2 --> [x = 0,
+y = 13,
+z = 45,
+s = ["abc", "xyz"],
+e = en6], ref 3 --> [x = 0,
+y = 13,
+z = 45,
+s = ["abc", "xyz"],
+e = en6]]""")
+  doAssert(repr(en4) == "en4")
+
+  doAssert(repr({'a'..'p'}) == "{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}")
diff --git a/tests/js/treprinifexpr.nim b/tests/js/treprinifexpr.nim
new file mode 100644
index 000000000..09ded18b9
--- /dev/null
+++ b/tests/js/treprinifexpr.nim
@@ -0,0 +1,18 @@
+type
+  Enum = enum A
+
+let
+  enumVal = A
+  tmp = if true: $enumVal else: $enumVal
+
+let
+  intVal = 12
+  tmp2 = if true: repr(intVal) else: $enumVal
+
+let
+  strVal = "123"
+  tmp3 = if true: repr(strVal) else: $strVal
+
+let
+  floatVal = 12.4
+  tmp4 = if true: repr(floatVal) else: $floatVal
\ No newline at end of file
diff --git a/tests/js/tseqops.nim b/tests/js/tseqops.nim
new file mode 100644
index 000000000..8cfb50886
--- /dev/null
+++ b/tests/js/tseqops.nim
@@ -0,0 +1,48 @@
+# bug #4139
+
+type
+  TestO = object
+    x, y: int
+
+proc onLoad() =
+  var test: seq[TestO] = @[]
+  var foo = TestO(x: 0, y: 0)
+  test.add(foo)
+  foo.x = 5
+  doAssert $test[0] == "(x: 0, y: 0)"
+  doAssert $foo == "(x: 5, y: 0)"
+
+onLoad()
+
+# 'setLen' bug (part of bug #5933)
+type MyObj = object
+  x: cstring
+  y: int
+
+proc foo(x: var seq[MyObj]) =
+  let L = x.len
+  x.setLen L + 1
+  x[L] = x[1]
+
+var s = @[MyObj(x: "2", y: 4), MyObj(x: "4", y: 5)]
+foo(s)
+doAssert $s == """@[(x: "2", y: 4), (x: "4", y: 5), (x: "4", y: 5)]"""
+
+# bug  #5933
+import sequtils
+
+type
+  Test = object
+    a: cstring
+    b: int
+
+var test = @[Test(a: "1", b: 1), Test(a: "2", b: 2)]
+
+test.insert(@[Test(a: "3", b: 3)], 0)
+
+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
new file mode 100644
index 000000000..db851ba28
--- /dev/null
+++ b/tests/js/tstdlib_imports.nim
@@ -0,0 +1,80 @@
+discard """
+  action: compile
+"""
+
+{.warning[UnusedImport]: off.}
+
+when defined(nimPreviewSlimSystem):
+  import std/[
+    syncio, assertions, formatfloat, objectdollar, widestrs
+  ]
+
+import std/[
+  # Core:
+  bitops, typetraits, lenientops, macros, volatile, typeinfo,
+  # fails due to FFI: rlocks
+  # fails due to cstring cast/copyMem: endians
+  # works but uses FFI: cpuinfo, locks
+
+  # Algorithms:
+  algorithm, enumutils, sequtils, setutils,
+  
+  # Collections:
+  critbits, deques, heapqueue, intsets, lists, options, sets,
+  tables, packedsets,
+
+  # Strings:
+  cstrutils, editdistance, wordwrap, parseutils, ropes,
+  pegs, strformat, strmisc, strscans, strtabs,
+  strutils, unicode, unidecode,
+  # fails due to FFI: encodings
+
+  # Time handling:
+  monotimes, times,
+
+  # Generic operator system services:
+  os, streams,
+  # fails intentionally: dynlib, marshal, memfiles
+  # fails due to FFI: osproc, terminal
+  # fails due to osproc import: distros
+
+  # Math libraries:
+  complex, math, random, rationals, stats, sums, sysrand,
+  # works but uses FFI: fenv
+
+  # Internet protocols:
+  cookies, httpcore, mimetypes, uri,
+  # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver,
+  # asyncnet, cgi, httpclient, nativesockets, net, selectors
+  # works but no need to test: asyncstreams, asyncfutures
+  
+  # Threading:
+  # fails due to FFI: threadpool
+
+  # Parsers:
+  htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml,
+  parseopt, jsonutils,
+
+  # XML processing:
+  xmltree, xmlparser,
+
+  # Generators:
+  htmlgen,
+
+  # Hashing:
+  base64, hashes,
+  # fails due to cstring cast/endians import: oids
+  # fails due to copyMem/endians import: sha1
+
+  # Miscellaneous:
+  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, jsbigints,
+
+  # Unlisted in lib.html:
+  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
new file mode 100644
index 000000000..1e584f735
--- /dev/null
+++ b/tests/js/tstdlib_various.nim
@@ -0,0 +1,174 @@
+discard """
+output: '''
+abc
+def
+definition
+prefix
+xyz
+def
+definition
+Hi Andreas! How do you feel, Rumpf?
+
+@[0, 2, 1]
+@[1, 0, 2]
+@[1, 2, 0]
+@[2, 0, 1]
+@[2, 1, 0]
+@[2, 0, 1]
+@[1, 2, 0]
+@[1, 0, 2]
+@[0, 2, 1]
+@[0, 1, 2]
+[5]
+[4, 5]
+[3, 4, 5]
+[2, 3, 4, 5]
+[2, 3, 4, 5, 6]
+[1, 2, 3, 4, 5, 6]
+'''
+"""
+
+import
+  critbits, sets, strutils, tables, random, algorithm, ropes,
+  lists, htmlgen, xmltree, strtabs
+
+
+block tcritbits:
+  var r: CritBitTree[void]
+  r.incl "abc"
+  r.incl "xyz"
+  r.incl "def"
+  r.incl "definition"
+  r.incl "prefix"
+  doAssert r.contains"def"
+  #r.del "def"
+
+  for w in r.items:
+    echo w
+  for w in r.itemsWithPrefix("de"):
+    echo w
+
+
+
+block testequivalence:
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "equivalent or subset")
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3]), "equivalent or subset")
+  doAssert((not(toHashSet(@[1,2,3]) <= toHashSet(@[1,2]))), "equivalent or subset")
+  doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2,3]))), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2]))), "strict subset")
+  doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3,4]))), "==")
+  doAssert(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3]), "==")
+  doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2]))), "==")
+
+
+
+block tformat:
+  echo("Hi $1! How do you feel, $2?\n" % ["Andreas", "Rumpf"])
+
+
+
+block tnilecho:
+  var x = @["1", "", "3"]
+  doAssert $x == """@["1", "", "3"]"""
+
+
+
+block torderedtable:
+  var t = initOrderedTable[int,string]()
+
+  # this tests issue #5917
+  var data = newSeq[int]()
+  for i in 0..<1000:
+    var x = rand(1000)
+    if x notin t: data.add(x)
+    t[x] = "meh"
+
+  # this checks that keys are re-inserted
+  # in order when table is enlarged.
+  var i = 0
+  for k, v in t:
+    doAssert(k == data[i])
+    doAssert(v == "meh")
+    inc(i)
+
+
+
+block tpermutations:
+  var v = @[0, 1, 2]
+  while v.nextPermutation():
+    echo v
+  while v.prevPermutation():
+    echo v
+
+
+block tropes:
+  var
+    r1 = rope("")
+    r2 = rope("123")
+  doAssert r1.len == 0
+  doAssert r2.len == 3
+  doAssert $r1 == ""
+  doAssert $r2 == "123"
+
+  r1.add("123")
+  r2.add("456")
+  doAssert r1.len == 3
+  doAssert r2.len == 6
+  doAssert $r1 == "123"
+  doAssert $r2 == "123456"
+  doAssert $r1[1] == "2"
+  doAssert $r2[2] == "3"
+
+
+block tsinglylinkedring:
+  var r = initSinglyLinkedRing[int]()
+  r.prepend(5)
+  echo r
+  r.prepend(4)
+  echo r
+  r.prepend(3)
+  echo r
+  r.prepend(2)
+  echo r
+  r.append(6)
+  echo r
+  r.prepend(1)
+  echo r
+
+block tsplit:
+  var s = ""
+  for w in split("|abc|xy|z", {'|'}):
+    s.add("#")
+    s.add(w)
+
+  doAssert s == "##abc#xy#z"
+
+block tsplit2:
+  var s = ""
+  for w in split("|abc|xy|z", {'|'}):
+    s.add("#")
+    s.add(w)
+
+  doAssert "true".split("") == @["true"]
+
+block txmlgen:
+  var nim = "Nim"
+  doAssert h1(a(href="http://force7.de/nim", nim)) ==
+    "<h1><a href=\"http://force7.de/nim\">Nim</a></h1>"
+
+block txmltree:
+  var x = <>a(href="nim.de", newText("www.nim-test.de"))
+
+  doAssert($x == "<a href=\"nim.de\">www.nim-test.de</a>")
+  doAssert(newText("foo").innerText == "foo")
+  doAssert(newEntity("bar").innerText == "bar")
+  doAssert(newComment("baz").innerText == "")
+
+  let y = newXmlTree("x", [
+    newText("foo"),
+    newXmlTree("y", [
+      newText("bar")
+    ])
+  ])
+  doAssert(y.innerText == "foobar")
diff --git a/tests/js/tstreams.nim b/tests/js/tstreams.nim
new file mode 100644
index 000000000..43c26e01a
--- /dev/null
+++ b/tests/js/tstreams.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''
+I
+AM
+GROOT
+'''
+"""
+
+import streams
+
+var s = newStringStream("I\nAM\nGROOT")
+doAssert s.peekStr(1) == "I"
+doAssert s.peekChar() == 'I'
+for line in s.lines:
+  echo line
+s.close
+
+var s2 = newStringStream("abc")
+doAssert s2.readAll == "abc"
+s2.write("def")
+doAssert s2.data == "abcdef"
+s2.close
diff --git a/tests/js/tstring_assignment.nim b/tests/js/tstring_assignment.nim
new file mode 100644
index 000000000..97ffa748f
--- /dev/null
+++ b/tests/js/tstring_assignment.nim
@@ -0,0 +1,21 @@
+discard """
+  output: '''true
+asdfasekjkler'''
+"""
+
+# bug #4471
+when true:
+  let s1 = "123"
+  var s2 = s1
+  s2.setLen(0)
+  # fails - s1.len == 0
+  echo s1.len == 3
+
+# bug #4470
+proc main(s: cstring): string =
+  result = newString(0)
+  for i in 0..<s.len:
+    if s[i] >= 'a' and s[i] <= 'z':
+      result.add s[i]
+
+echo main("asdfasekjkleräöü")
diff --git a/tests/js/tstringitems.nim b/tests/js/tstringitems.nim
new file mode 100644
index 000000000..16df04149
--- /dev/null
+++ b/tests/js/tstringitems.nim
@@ -0,0 +1,95 @@
+discard """
+  output: '''Hello
+Hello
+c
+d
+e'''
+"""
+
+block: # bug #2581
+  const someVars = [ "Hello" ]
+  var someVars2 = [ "Hello" ]
+
+  proc getSomeVar: string =
+      for i in someVars:
+          if i == "Hello":
+              result = i
+              break
+
+  proc getSomeVar2: string =
+      for i in someVars2:
+          if i == "Hello":
+              result = i
+              break
+
+  echo getSomeVar()
+  echo getSomeVar2()
+
+block: # Test compile-time binary data generation, invalid unicode
+  proc signatureMaker(): string {. compiletime .} =
+    const signatureBytes = [137, 80, 78, 71, 13, 10, 26, 10]
+    result = ""
+    for c in signatureBytes: result.add chr(c)
+
+  const cSig = signatureMaker()
+
+  var rSig = newString(8)
+  rSig[0] = chr(137)
+  rSig[1] = chr(80)
+  rSig[2] = chr(78)
+  rSig[3] = chr(71)
+  rSig[4] = chr(13)
+  rSig[5] = chr(10)
+  rSig[6] = chr(26)
+  rSig[7] = chr(10)
+
+  doAssert(rSig == cSig)
+
+block: # Test unicode strings
+  const constStr = "Привет!"
+  var jsStr : cstring
+  {.emit: """`jsStr` = "Привет!";""".}
+
+  doAssert($jsStr == constStr)
+  var runtimeStr = "При"
+  runtimeStr &= "вет!"
+
+  doAssert(runtimeStr == constStr)
+
+block: # Conversions from/to cstring
+  proc stringSaysHelloInRussian(s: cstring): bool =
+    {.emit: """`result` = (`s` === "Привет!");""".}
+
+  doAssert(stringSaysHelloInRussian("Привет!"))
+
+  const constStr = "Привет!"
+  doAssert(stringSaysHelloInRussian(constStr))
+
+  var rtStr = "Привет!"
+  doAssert(stringSaysHelloInRussian(rtStr))
+
+block: # String case of
+  const constStr = "Привет!"
+  var s = "Привет!"
+
+  case s
+  of constStr: discard
+  else: doAssert(false)
+
+  case s
+  of "Привет!": discard
+  else: doAssert(false)
+
+block: # String cmp
+  var a, b: string
+  doAssert(cmp(a, b) == 0)
+  doAssert(cmp("foo", "foo") == 0)
+  doAssert(cmp("foobar", "foo") == 3)
+  doAssert(cmp("foo", "foobar") == -3)
+  doAssert(cmp("fooz", "foog") == 19)
+  doAssert(cmp("foog", "fooz") == -19)
+
+proc main(x: openArray[char]) =
+  for c in x: echo c
+
+main(toOpenArray(['a', 'b', 'c', 'd', 'e'], 2, 4))
diff --git a/tests/js/ttempgen.nim b/tests/js/ttempgen.nim
new file mode 100644
index 000000000..badc66c1b
--- /dev/null
+++ b/tests/js/ttempgen.nim
@@ -0,0 +1,79 @@
+discard """
+  output: '''
+foo
+'''
+"""
+
+block: # #12672
+  var a = @[1]
+  let i = 1
+  inc a[i-1]
+
+  var b: seq[int]
+  doAssertRaises(IndexDefect): inc b[0]
+  doAssertRaises(IndexDefect): inc b[i-1]
+
+  var x: seq[seq[int]]
+  doAssertRaises(IndexDefect): # not TypeError
+    inc x[0][i-1]
+
+block: # #14087
+  type Obj = object
+    str: string
+
+  var s = @[Obj(str: "abc"), Obj(str: "def")]
+  s[1].str.add("ghi")
+  s[s.len - 1].str.add("jkl")
+  s[^1].str.add("mno")
+  s[s.high].str.add("pqr")
+
+  let slen = s.len
+  s[slen - 1].str.add("stu")
+
+  let shigh = s.high
+  s[shigh].str.add("vwx")
+
+  proc foo(): int =
+    echo "foo"
+    shigh
+  s[foo()].str.add("yz")
+  doAssert s[1].str == "defghijklmnopqrstuvwxyz"
+
+block: # #14117
+  type
+    A = object
+      case kind: bool
+      of true:
+        sons: seq[int]
+      else: discard
+
+  var a = A(kind: true)
+  doAssert a.sons.len == 0
+  a.sons.add(1)
+  doAssert a.sons.len == 1
+
+import tables
+
+block: # #13966
+  var t: Table[int8, array[int8, seq[tuple[]]]]
+
+  t[0] = default(array[int8, seq[tuple[]]])
+  t[0][0].add ()
+
+block: # #11783
+  proc fun(): string =
+    discard
+
+  var ret: string
+  ret.add fun()
+  doAssert ret == ""
+
+block: # #12256
+  var x: bool
+
+  doAssert x == false
+
+  reset x
+
+  doAssert x == false
+  doAssert x != true
diff --git a/tests/js/tthismangle.nim b/tests/js/tthismangle.nim
new file mode 100644
index 000000000..880abcc83
--- /dev/null
+++ b/tests/js/tthismangle.nim
@@ -0,0 +1,23 @@
+proc moo1(this: int) =
+  doAssert this == 42
+
+proc moo2(x: int) =
+  var this = x
+  doAssert this == 42
+
+proc moo3() =
+  for this in [1,1,1]:
+    doAssert this == 1
+
+proc moo4() =
+  type
+    X = object
+      this: int
+
+  var q = X(this: 42)
+  doAssert q.this == 42
+
+moo1(42)
+moo2(42)
+moo3()
+moo4()
diff --git a/tests/js/ttryexceptnewsyntax.nim b/tests/js/ttryexceptnewsyntax.nim
new file mode 100644
index 000000000..2573c3727
--- /dev/null
+++ b/tests/js/ttryexceptnewsyntax.nim
@@ -0,0 +1,13 @@
+discard """
+  output: '''hello'''
+"""
+
+type
+  MyException = ref Exception
+
+#bug #5986
+
+try:
+  raise MyException(msg: "hello")
+except MyException as e:
+  echo e.msg
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/tunion.nim b/tests/js/tunion.nim
new file mode 100644
index 000000000..e185495ad
--- /dev/null
+++ b/tests/js/tunion.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "`{.union.}` is not implemented for js backend."
+"""
+
+type Foo {.union.} = object
+  as_bytes: array[8, int8]
+  data: int64
diff --git a/tests/js/tunittest_error.nim b/tests/js/tunittest_error.nim
new file mode 100644
index 000000000..781e34338
--- /dev/null
+++ b/tests/js/tunittest_error.nim
@@ -0,0 +1,24 @@
+discard """
+  exitcode: 1
+  outputsub: "[FAILED] with exception"
+"""
+
+# see also: `tests/stdlib/tunittest_error.nim`
+
+import unittest
+
+proc ddd() =
+  raise newException(IOError, "didn't do stuff")
+
+proc ccc() =
+  ddd()
+
+proc bbb() =
+  ccc()
+
+proc aaa() =
+  bbb()
+
+test "with exception":
+  check 3 == 3
+  aaa()
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]
diff --git a/tests/js/tvarargs.nim b/tests/js/tvarargs.nim
new file mode 100644
index 000000000..8d57af58d
--- /dev/null
+++ b/tests/js/tvarargs.nim
@@ -0,0 +1,15 @@
+discard """
+  output: "Hello, world"
+"""
+
+# bug #3584
+
+type
+  ConsoleObj {.importc.} = object of RootObj
+    log*: proc() {.nimcall varargs.}
+  Console = ref ConsoleObj
+
+var console* {.importc.}: Console
+
+when true:
+  console.log "Hello, world"
diff --git a/tests/js/twritestacktrace.nim b/tests/js/twritestacktrace.nim
new file mode 100644
index 000000000..2fe2b1987
--- /dev/null
+++ b/tests/js/twritestacktrace.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim js --panics:on $file"
+  output: '''Traceback (most recent call last)
+twritestacktrace.nim(12) at module twritestacktrace
+twritestacktrace.nim(10) at twritestacktrace.hello
+'''
+"""
+
+proc hello() =
+  writeStackTrace()
+
+hello()