summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/ports/webassembly/basic.js8
-rw-r--r--tests/ports/webassembly/basic.js.exp3
-rw-r--r--tests/ports/webassembly/filesystem.mjs9
-rw-r--r--tests/ports/webassembly/filesystem.mjs.exp3
-rw-r--r--tests/ports/webassembly/float.mjs43
-rw-r--r--tests/ports/webassembly/float.mjs.exp14
-rw-r--r--tests/ports/webassembly/fun_call.mjs17
-rw-r--r--tests/ports/webassembly/fun_call.mjs.exp7
-rw-r--r--tests/ports/webassembly/globals.mjs13
-rw-r--r--tests/ports/webassembly/globals.mjs.exp4
-rw-r--r--tests/ports/webassembly/heap_expand.mjs15
-rw-r--r--tests/ports/webassembly/heap_expand.mjs.exp48
-rw-r--r--tests/ports/webassembly/jsffi_create_proxy.mjs15
-rw-r--r--tests/ports/webassembly/jsffi_create_proxy.mjs.exp6
-rw-r--r--tests/ports/webassembly/jsffi_to_js.mjs28
-rw-r--r--tests/ports/webassembly/jsffi_to_js.mjs.exp11
-rw-r--r--tests/ports/webassembly/override_new.mjs33
-rw-r--r--tests/ports/webassembly/override_new.mjs.exp7
-rw-r--r--tests/ports/webassembly/promise_with_resolvers.mjs23
-rw-r--r--tests/ports/webassembly/promise_with_resolvers.mjs.exp1
-rw-r--r--tests/ports/webassembly/py_proxy_delete.mjs30
-rw-r--r--tests/ports/webassembly/py_proxy_delete.mjs.exp2
-rw-r--r--tests/ports/webassembly/py_proxy_dict.mjs34
-rw-r--r--tests/ports/webassembly/py_proxy_dict.mjs.exp11
-rw-r--r--tests/ports/webassembly/py_proxy_has.mjs11
-rw-r--r--tests/ports/webassembly/py_proxy_has.mjs.exp2
-rw-r--r--tests/ports/webassembly/py_proxy_own_keys.mjs11
-rw-r--r--tests/ports/webassembly/py_proxy_own_keys.mjs.exp9
-rw-r--r--tests/ports/webassembly/py_proxy_set.mjs27
-rw-r--r--tests/ports/webassembly/py_proxy_set.mjs.exp2
-rw-r--r--tests/ports/webassembly/py_proxy_to_js.mjs20
-rw-r--r--tests/ports/webassembly/py_proxy_to_js.mjs.exp4
-rw-r--r--tests/ports/webassembly/register_js_module.js6
-rw-r--r--tests/ports/webassembly/register_js_module.js.exp2
-rw-r--r--tests/ports/webassembly/run_python_async.mjs115
-rw-r--r--tests/ports/webassembly/run_python_async.mjs.exp38
-rw-r--r--tests/ports/webassembly/run_python_async2.mjs42
-rw-r--r--tests/ports/webassembly/run_python_async2.mjs.exp8
-rw-r--r--tests/ports/webassembly/this_behaviour.mjs24
-rw-r--r--tests/ports/webassembly/this_behaviour.mjs.exp4
-rw-r--r--tests/ports/webassembly/various.js38
-rw-r--r--tests/ports/webassembly/various.js.exp8
-rwxr-xr-xtests/run-tests.py2
43 files changed, 757 insertions, 1 deletions
diff --git a/tests/ports/webassembly/basic.js b/tests/ports/webassembly/basic.js
new file mode 100644
index 000000000..19b1881fa
--- /dev/null
+++ b/tests/ports/webassembly/basic.js
@@ -0,0 +1,8 @@
+import(process.argv[2]).then((mp) => {
+ mp.loadMicroPython().then((py) => {
+ py.runPython("1");
+ py.runPython("print('hello')");
+ py.runPython("import sys; print(f'hello from {sys.platform}')");
+ py.runPython("import collections; print(collections.OrderedDict)");
+ });
+});
diff --git a/tests/ports/webassembly/basic.js.exp b/tests/ports/webassembly/basic.js.exp
new file mode 100644
index 000000000..6459b2492
--- /dev/null
+++ b/tests/ports/webassembly/basic.js.exp
@@ -0,0 +1,3 @@
+hello
+hello from webassembly
+<class 'OrderedDict'>
diff --git a/tests/ports/webassembly/filesystem.mjs b/tests/ports/webassembly/filesystem.mjs
new file mode 100644
index 000000000..e9e2920a1
--- /dev/null
+++ b/tests/ports/webassembly/filesystem.mjs
@@ -0,0 +1,9 @@
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.FS.mkdir("/lib/");
+mp.FS.writeFile("/lib/testmod.py", "x = 1; print(__name__, x)");
+mp.runPython("import testmod");
+
+mp.runPython("import sys; sys.modules.clear()");
+const testmod = mp.pyimport("testmod");
+console.log("testmod:", testmod, testmod.x);
diff --git a/tests/ports/webassembly/filesystem.mjs.exp b/tests/ports/webassembly/filesystem.mjs.exp
new file mode 100644
index 000000000..25a48b108
--- /dev/null
+++ b/tests/ports/webassembly/filesystem.mjs.exp
@@ -0,0 +1,3 @@
+testmod 1
+testmod 1
+testmod: PyProxy { _ref: 3 } 1
diff --git a/tests/ports/webassembly/float.mjs b/tests/ports/webassembly/float.mjs
new file mode 100644
index 000000000..53bb8b1c4
--- /dev/null
+++ b/tests/ports/webassembly/float.mjs
@@ -0,0 +1,43 @@
+// Test passing floats between JavaScript and Python.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+globalThis.a = 1 / 2;
+globalThis.b = Infinity;
+globalThis.c = NaN;
+
+mp.runPython(`
+import js
+
+# Test retrieving floats from JS.
+print(js.a)
+print(js.b)
+print(js.c)
+
+# Test calling JS which returns a float.
+r = js.Math.random()
+print(type(r), 0 < r < 1)
+
+x = 1 / 2
+y = float("inf")
+z = float("nan")
+
+# Test passing floats to a JS function.
+js.console.log(x)
+js.console.log(x, y)
+js.console.log(x, y, z)
+`);
+
+// Test retrieving floats from Python.
+console.log(mp.globals.get("x"));
+console.log(mp.globals.get("y"));
+console.log(mp.globals.get("z"));
+
+// Test passing floats to a Python function.
+const mp_print = mp.pyimport("builtins").print;
+mp_print(globalThis.a);
+mp_print(globalThis.a, globalThis.b);
+mp_print(globalThis.a, globalThis.b, globalThis.c);
+
+// Test calling Python which returns a float.
+console.log(mp.pyimport("math").sqrt(0.16));
diff --git a/tests/ports/webassembly/float.mjs.exp b/tests/ports/webassembly/float.mjs.exp
new file mode 100644
index 000000000..57eff74ac
--- /dev/null
+++ b/tests/ports/webassembly/float.mjs.exp
@@ -0,0 +1,14 @@
+0.5
+inf
+nan
+<class 'float'> True
+0.5
+0.5 Infinity
+0.5 Infinity NaN
+0.5
+Infinity
+NaN
+0.5
+0.5 inf
+0.5 inf nan
+0.4
diff --git a/tests/ports/webassembly/fun_call.mjs b/tests/ports/webassembly/fun_call.mjs
new file mode 100644
index 000000000..295745d2e
--- /dev/null
+++ b/tests/ports/webassembly/fun_call.mjs
@@ -0,0 +1,17 @@
+// Test calling JavaScript functions from Python.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+globalThis.f = (a, b, c, d, e) => {
+ console.log(a, b, c, d, e);
+};
+mp.runPython(`
+import js
+js.f()
+js.f(1)
+js.f(1, 2)
+js.f(1, 2, 3)
+js.f(1, 2, 3, 4)
+js.f(1, 2, 3, 4, 5)
+js.f(1, 2, 3, 4, 5, 6)
+`);
diff --git a/tests/ports/webassembly/fun_call.mjs.exp b/tests/ports/webassembly/fun_call.mjs.exp
new file mode 100644
index 000000000..e9ed5f6dd
--- /dev/null
+++ b/tests/ports/webassembly/fun_call.mjs.exp
@@ -0,0 +1,7 @@
+undefined undefined undefined undefined undefined
+1 undefined undefined undefined undefined
+1 2 undefined undefined undefined
+1 2 3 undefined undefined
+1 2 3 4 undefined
+1 2 3 4 5
+1 2 3 4 5
diff --git a/tests/ports/webassembly/globals.mjs b/tests/ports/webassembly/globals.mjs
new file mode 100644
index 000000000..3d5ecb416
--- /dev/null
+++ b/tests/ports/webassembly/globals.mjs
@@ -0,0 +1,13 @@
+// Test accessing Python globals dict via mp.globals.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython("x = 1");
+console.log(mp.globals.get("x"));
+
+mp.globals.set("y", 2);
+mp.runPython("print(y)");
+
+mp.runPython("print('y' in globals())");
+mp.globals.delete("y");
+mp.runPython("print('y' in globals())");
diff --git a/tests/ports/webassembly/globals.mjs.exp b/tests/ports/webassembly/globals.mjs.exp
new file mode 100644
index 000000000..a118c13fe
--- /dev/null
+++ b/tests/ports/webassembly/globals.mjs.exp
@@ -0,0 +1,4 @@
+1
+2
+True
+False
diff --git a/tests/ports/webassembly/heap_expand.mjs b/tests/ports/webassembly/heap_expand.mjs
new file mode 100644
index 000000000..2cf7c07b0
--- /dev/null
+++ b/tests/ports/webassembly/heap_expand.mjs
@@ -0,0 +1,15 @@
+// Test expanding the MicroPython GC heap.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+import gc
+bs = []
+for i in range(24):
+ b = bytearray(1 << i)
+ bs.append(b)
+ gc.collect()
+ print(gc.mem_free())
+for b in bs:
+ print(len(b))
+`);
diff --git a/tests/ports/webassembly/heap_expand.mjs.exp b/tests/ports/webassembly/heap_expand.mjs.exp
new file mode 100644
index 000000000..ee1490840
--- /dev/null
+++ b/tests/ports/webassembly/heap_expand.mjs.exp
@@ -0,0 +1,48 @@
+135241360
+135241328
+135241296
+135241264
+135241216
+135241168
+135241088
+135240944
+135240640
+135240112
+135239072
+135237008
+135232896
+135224688
+135208288
+135175504
+135109888
+134978800
+134716640
+135216848
+136217216
+138218032
+142219616
+150222864
+1
+2
+4
+8
+16
+32
+64
+128
+256
+512
+1024
+2048
+4096
+8192
+16384
+32768
+65536
+131072
+262144
+524288
+1048576
+2097152
+4194304
+8388608
diff --git a/tests/ports/webassembly/jsffi_create_proxy.mjs b/tests/ports/webassembly/jsffi_create_proxy.mjs
new file mode 100644
index 000000000..5f04bf44d
--- /dev/null
+++ b/tests/ports/webassembly/jsffi_create_proxy.mjs
@@ -0,0 +1,15 @@
+// Test jsffi.create_proxy().
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+import jsffi
+x = jsffi.create_proxy(1)
+print(x)
+y = jsffi.create_proxy([2])
+print(y)
+`);
+console.log(mp.globals.get("x"));
+console.log(mp.PyProxy.toJs(mp.globals.get("x")));
+console.log(mp.globals.get("y"));
+console.log(mp.PyProxy.toJs(mp.globals.get("y")));
diff --git a/tests/ports/webassembly/jsffi_create_proxy.mjs.exp b/tests/ports/webassembly/jsffi_create_proxy.mjs.exp
new file mode 100644
index 000000000..a3b38a78b
--- /dev/null
+++ b/tests/ports/webassembly/jsffi_create_proxy.mjs.exp
@@ -0,0 +1,6 @@
+1
+<JsProxy 1>
+1
+1
+PyProxy { _ref: 3 }
+[ 2 ]
diff --git a/tests/ports/webassembly/jsffi_to_js.mjs b/tests/ports/webassembly/jsffi_to_js.mjs
new file mode 100644
index 000000000..714af6b62
--- /dev/null
+++ b/tests/ports/webassembly/jsffi_to_js.mjs
@@ -0,0 +1,28 @@
+// Test jsffi.to_js().
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+import jsffi
+x = jsffi.to_js(1)
+print(x)
+y = jsffi.to_js([2])
+print(y)
+z = jsffi.to_js({"three":3})
+print(z)
+`);
+
+const x = mp.globals.get("x");
+const y = mp.globals.get("y");
+const z = mp.globals.get("z");
+
+console.log(Array.isArray(x));
+console.log(x);
+
+console.log(Array.isArray(y));
+console.log(y);
+console.log(Reflect.ownKeys(y));
+
+console.log(Array.isArray(z));
+console.log(z);
+console.log(Reflect.ownKeys(z));
diff --git a/tests/ports/webassembly/jsffi_to_js.mjs.exp b/tests/ports/webassembly/jsffi_to_js.mjs.exp
new file mode 100644
index 000000000..399dd0aa8
--- /dev/null
+++ b/tests/ports/webassembly/jsffi_to_js.mjs.exp
@@ -0,0 +1,11 @@
+1
+<JsProxy 1>
+<JsProxy 2>
+false
+1
+true
+[ 2 ]
+[ '0', 'length' ]
+false
+{ three: 3 }
+[ 'three' ]
diff --git a/tests/ports/webassembly/override_new.mjs b/tests/ports/webassembly/override_new.mjs
new file mode 100644
index 000000000..f5d64d7d1
--- /dev/null
+++ b/tests/ports/webassembly/override_new.mjs
@@ -0,0 +1,33 @@
+// Test overriding .new() on a JavaScript class.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+globalThis.MyClass1 = class {
+ new() {
+ console.log("MyClass1 new");
+ return 1;
+ }
+};
+
+globalThis.MyClass2 = class {
+ static new() {
+ console.log("MyClass2 static new");
+ return 2;
+ }
+ new() {
+ console.log("MyClass2 new");
+ return 3;
+ }
+};
+
+globalThis.myClass2Instance = new globalThis.MyClass2();
+
+mp.runPython(`
+ import js
+
+ print(type(js.MyClass1.new()))
+ print(js.MyClass1.new().new())
+
+ print(js.MyClass2.new())
+ print(js.myClass2Instance.new())
+`);
diff --git a/tests/ports/webassembly/override_new.mjs.exp b/tests/ports/webassembly/override_new.mjs.exp
new file mode 100644
index 000000000..2efb66971
--- /dev/null
+++ b/tests/ports/webassembly/override_new.mjs.exp
@@ -0,0 +1,7 @@
+<class 'JsProxy'>
+MyClass1 new
+1
+MyClass2 static new
+2
+MyClass2 new
+3
diff --git a/tests/ports/webassembly/promise_with_resolvers.mjs b/tests/ports/webassembly/promise_with_resolvers.mjs
new file mode 100644
index 000000000..a2c6d509a
--- /dev/null
+++ b/tests/ports/webassembly/promise_with_resolvers.mjs
@@ -0,0 +1,23 @@
+// Test polyfill of a method on a built-in.
+
+// Implement Promise.withResolvers, and make sure it has a unique name so
+// the test below is guaranteed to use this version.
+Promise.withResolversCustom = function withResolversCustom() {
+ let a;
+ let b;
+ const c = new this((resolve, reject) => {
+ a = resolve;
+ b = reject;
+ });
+ return { resolve: a, reject: b, promise: c };
+};
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+ from js import Promise
+
+ deferred = Promise.withResolversCustom()
+ deferred.promise.then(print)
+ deferred.resolve('OK')
+`);
diff --git a/tests/ports/webassembly/promise_with_resolvers.mjs.exp b/tests/ports/webassembly/promise_with_resolvers.mjs.exp
new file mode 100644
index 000000000..d86bac9de
--- /dev/null
+++ b/tests/ports/webassembly/promise_with_resolvers.mjs.exp
@@ -0,0 +1 @@
+OK
diff --git a/tests/ports/webassembly/py_proxy_delete.mjs b/tests/ports/webassembly/py_proxy_delete.mjs
new file mode 100644
index 000000000..2afea0b9f
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_delete.mjs
@@ -0,0 +1,30 @@
+// Test `delete <py-obj>.<attr>` on the JavaScript side, which tests PyProxy.deleteProperty.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+class A:
+ pass
+x = A()
+x.foo = 1
+y = []
+`);
+
+const x = mp.globals.get("x");
+const y = mp.globals.get("y");
+
+// Should pass.
+// biome-ignore lint/performance/noDelete: test delete statement
+delete x.foo;
+
+mp.runPython(`
+print(hasattr(x, "foo"))
+`);
+
+// Should fail, can't delete attributes on MicroPython lists.
+try {
+ // biome-ignore lint/performance/noDelete: test delete statement
+ delete y.sort;
+} catch (error) {
+ console.log(error.message);
+}
diff --git a/tests/ports/webassembly/py_proxy_delete.mjs.exp b/tests/ports/webassembly/py_proxy_delete.mjs.exp
new file mode 100644
index 000000000..8eb9ad150
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_delete.mjs.exp
@@ -0,0 +1,2 @@
+False
+'deleteProperty' on proxy: trap returned falsish for property 'sort'
diff --git a/tests/ports/webassembly/py_proxy_dict.mjs b/tests/ports/webassembly/py_proxy_dict.mjs
new file mode 100644
index 000000000..201f179ef
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_dict.mjs
@@ -0,0 +1,34 @@
+// Test passing a Python dict into JavaScript, it should act like a JS object.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+x = {"a": 1, "b": 2}
+`);
+
+const x = mp.globals.get("x");
+
+// Test has, get, keys/iteration.
+console.log("a" in x, "b" in x, "c" in x);
+console.log(x.a, x.b);
+for (const k in x) {
+ console.log(k, x[k]);
+}
+console.log(Object.keys(x));
+console.log(Reflect.ownKeys(x));
+
+// Test set.
+x.c = 3;
+console.log(Object.keys(x));
+
+// Test delete.
+// biome-ignore lint/performance/noDelete: test delete statement
+delete x.b;
+console.log(Object.keys(x));
+
+// Make sure changes on the JavaScript side are reflected in Python.
+mp.runPython(`
+print(x["a"])
+print("b" in x)
+print(x["c"])
+`);
diff --git a/tests/ports/webassembly/py_proxy_dict.mjs.exp b/tests/ports/webassembly/py_proxy_dict.mjs.exp
new file mode 100644
index 000000000..f0e15034b
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_dict.mjs.exp
@@ -0,0 +1,11 @@
+true true false
+1 2
+a 1
+b 2
+[ 'a', 'b' ]
+[ 'a', 'b' ]
+[ 'a', 'c', 'b' ]
+[ 'a', 'c' ]
+1
+False
+3
diff --git a/tests/ports/webassembly/py_proxy_has.mjs b/tests/ports/webassembly/py_proxy_has.mjs
new file mode 100644
index 000000000..8881776fd
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_has.mjs
@@ -0,0 +1,11 @@
+// Test `<attr> in <py-obj>` on the JavaScript side, which tests PyProxy.has.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+x = []
+`);
+
+const x = mp.globals.get("x");
+console.log("no_exist" in x);
+console.log("sort" in x);
diff --git a/tests/ports/webassembly/py_proxy_has.mjs.exp b/tests/ports/webassembly/py_proxy_has.mjs.exp
new file mode 100644
index 000000000..1d474d525
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_has.mjs.exp
@@ -0,0 +1,2 @@
+false
+true
diff --git a/tests/ports/webassembly/py_proxy_own_keys.mjs b/tests/ports/webassembly/py_proxy_own_keys.mjs
new file mode 100644
index 000000000..bbf5b4f01
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_own_keys.mjs
@@ -0,0 +1,11 @@
+// Test `Reflect.ownKeys(<py-obj>)` on the JavaScript side, which tests PyProxy.ownKeys.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+x = []
+y = {"a": 1}
+`);
+
+console.log(Reflect.ownKeys(mp.globals.get("x")));
+console.log(Reflect.ownKeys(mp.globals.get("y")));
diff --git a/tests/ports/webassembly/py_proxy_own_keys.mjs.exp b/tests/ports/webassembly/py_proxy_own_keys.mjs.exp
new file mode 100644
index 000000000..313d06d4d
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_own_keys.mjs.exp
@@ -0,0 +1,9 @@
+[
+ 'append', 'clear',
+ 'copy', 'count',
+ 'extend', 'index',
+ 'insert', 'pop',
+ 'remove', 'reverse',
+ 'sort'
+]
+[ 'a' ]
diff --git a/tests/ports/webassembly/py_proxy_set.mjs b/tests/ports/webassembly/py_proxy_set.mjs
new file mode 100644
index 000000000..30360a847
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_set.mjs
@@ -0,0 +1,27 @@
+// Test `<py-obj>.<attr> = <value>` on the JavaScript side, which tests PyProxy.set.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+class A:
+ pass
+x = A()
+y = []
+`);
+
+const x = mp.globals.get("x");
+const y = mp.globals.get("y");
+
+// Should pass.
+x.foo = 1;
+
+mp.runPython(`
+print(x.foo)
+`);
+
+// Should fail, can't set attributes on MicroPython lists.
+try {
+ y.bar = 1;
+} catch (error) {
+ console.log(error.message);
+}
diff --git a/tests/ports/webassembly/py_proxy_set.mjs.exp b/tests/ports/webassembly/py_proxy_set.mjs.exp
new file mode 100644
index 000000000..e1d995156
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_set.mjs.exp
@@ -0,0 +1,2 @@
+1
+'set' on proxy: trap returned falsish for property 'bar'
diff --git a/tests/ports/webassembly/py_proxy_to_js.mjs b/tests/ports/webassembly/py_proxy_to_js.mjs
new file mode 100644
index 000000000..f9fce606f
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_to_js.mjs
@@ -0,0 +1,20 @@
+// Test PyProxy.toJs().
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+mp.runPython(`
+a = 1
+b = (1, 2, 3)
+c = [None, True, 1.2]
+d = {"one": 1, "tuple": b, "list": c}
+`);
+
+const py_a = mp.globals.get("a");
+const py_b = mp.globals.get("b");
+const py_c = mp.globals.get("c");
+const py_d = mp.globals.get("d");
+
+console.log(py_a instanceof mp.PyProxy, mp.PyProxy.toJs(py_a));
+console.log(py_b instanceof mp.PyProxy, mp.PyProxy.toJs(py_b));
+console.log(py_c instanceof mp.PyProxy, mp.PyProxy.toJs(py_c));
+console.log(py_d instanceof mp.PyProxy, mp.PyProxy.toJs(py_d));
diff --git a/tests/ports/webassembly/py_proxy_to_js.mjs.exp b/tests/ports/webassembly/py_proxy_to_js.mjs.exp
new file mode 100644
index 000000000..279df7bdf
--- /dev/null
+++ b/tests/ports/webassembly/py_proxy_to_js.mjs.exp
@@ -0,0 +1,4 @@
+false 1
+true [ 1, 2, 3 ]
+true [ null, true, 1.2 ]
+true { tuple: [ 1, 2, 3 ], one: 1, list: [ null, true, 1.2 ] }
diff --git a/tests/ports/webassembly/register_js_module.js b/tests/ports/webassembly/register_js_module.js
new file mode 100644
index 000000000..b512f2c0d
--- /dev/null
+++ b/tests/ports/webassembly/register_js_module.js
@@ -0,0 +1,6 @@
+import(process.argv[2]).then((mp) => {
+ mp.loadMicroPython().then((py) => {
+ py.registerJsModule("js_module", { y: 2 });
+ py.runPython("import js_module; print(js_module); print(js_module.y)");
+ });
+});
diff --git a/tests/ports/webassembly/register_js_module.js.exp b/tests/ports/webassembly/register_js_module.js.exp
new file mode 100644
index 000000000..bb45f4ce0
--- /dev/null
+++ b/tests/ports/webassembly/register_js_module.js.exp
@@ -0,0 +1,2 @@
+<JsProxy 1>
+2
diff --git a/tests/ports/webassembly/run_python_async.mjs b/tests/ports/webassembly/run_python_async.mjs
new file mode 100644
index 000000000..44f2a90ab
--- /dev/null
+++ b/tests/ports/webassembly/run_python_async.mjs
@@ -0,0 +1,115 @@
+// Test runPythonAsync() and top-level await in Python.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+/**********************************************************/
+// Using only promise objects, no await's.
+
+console.log("= TEST 1 ==========");
+
+globalThis.p = new Promise((resolve, reject) => {
+ resolve(123);
+});
+
+console.log(1);
+
+mp.runPython(`
+import js
+print(js.p)
+print("py 1")
+print(js.p.then(lambda x: print("resolved", x)))
+print("py 2")
+`);
+
+console.log(2);
+
+// Let the promise resolve.
+await globalThis.p;
+
+console.log(3);
+
+/**********************************************************/
+// Using setTimeout to resolve the promise.
+
+console.log("= TEST 2 ==========");
+
+globalThis.p = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve(123);
+ console.log("setTimeout resolved");
+ }, 100);
+});
+
+console.log(1);
+
+mp.runPython(`
+import js
+print(js.p)
+print("py 1")
+print(js.p.then(lambda x: print("resolved", x)))
+print("py 2")
+`);
+
+console.log(2);
+
+// Let the promise resolve.
+await globalThis.p;
+
+console.log(3);
+
+/**********************************************************/
+// Using setTimeout and await within Python.
+
+console.log("= TEST 3 ==========");
+
+globalThis.p = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve(123);
+ console.log("setTimeout resolved");
+ }, 100);
+});
+
+console.log(1);
+
+const ret3 = await mp.runPythonAsync(`
+import js
+print("py 1")
+print("resolved value:", await js.p)
+print("py 2")
+`);
+
+console.log(2, ret3);
+
+/**********************************************************/
+// Multiple setTimeout's and await's within Python.
+
+console.log("= TEST 4 ==========");
+
+globalThis.p1 = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve(123);
+ console.log("setTimeout A resolved");
+ }, 100);
+});
+
+globalThis.p2 = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve(456);
+ console.log("setTimeout B resolved");
+ }, 200);
+});
+
+console.log(1);
+
+const ret4 = await mp.runPythonAsync(`
+import js
+print("py 1")
+print("resolved value:", await js.p1)
+print("py 2")
+print("resolved value:", await js.p1)
+print("py 3")
+print("resolved value:", await js.p2)
+print("py 4")
+`);
+
+console.log(2, ret4);
diff --git a/tests/ports/webassembly/run_python_async.mjs.exp b/tests/ports/webassembly/run_python_async.mjs.exp
new file mode 100644
index 000000000..f441bc5cf
--- /dev/null
+++ b/tests/ports/webassembly/run_python_async.mjs.exp
@@ -0,0 +1,38 @@
+= TEST 1 ==========
+1
+<JsProxy 1>
+py 1
+<JsProxy 4>
+py 2
+2
+resolved 123
+3
+= TEST 2 ==========
+1
+<JsProxy 5>
+py 1
+<JsProxy 8>
+py 2
+2
+setTimeout resolved
+resolved 123
+3
+= TEST 3 ==========
+1
+py 1
+setTimeout resolved
+resolved value: 123
+py 2
+2 null
+= TEST 4 ==========
+1
+py 1
+setTimeout A resolved
+resolved value: 123
+py 2
+resolved value: 123
+py 3
+setTimeout B resolved
+resolved value: 456
+py 4
+2 null
diff --git a/tests/ports/webassembly/run_python_async2.mjs b/tests/ports/webassembly/run_python_async2.mjs
new file mode 100644
index 000000000..87067e6e8
--- /dev/null
+++ b/tests/ports/webassembly/run_python_async2.mjs
@@ -0,0 +1,42 @@
+// Test runPythonAsync() and top-level await in Python, with multi-level awaits.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+// simulate a 2-step resolution of the string "OK".
+await mp.runPythonAsync(`
+import js
+
+def _timeout(resolve, _):
+ js.setTimeout(resolve, 100)
+
+def _fetch():
+ return js.Promise.new(_timeout)
+
+async def _text(promise):
+ if not promise._response:
+ print("_text await start")
+ await promise
+ print("_text awaited end")
+ ret = await promise._response.text()
+ return ret
+
+class _Response:
+ async def text(self):
+ print('_Response.text start')
+ await js.Promise.new(_timeout)
+ print('_Response.text end')
+ return "OK"
+
+def _response(promise):
+ promise._response = _Response()
+ return promise._response
+
+def fetch(url):
+ promise = _fetch().then(lambda *_: _response(promise))
+ promise._response = None
+ promise.text = lambda: _text(promise)
+ return promise
+
+print(await fetch("config.json").text())
+print(await (await fetch("config.json")).text())
+`);
diff --git a/tests/ports/webassembly/run_python_async2.mjs.exp b/tests/ports/webassembly/run_python_async2.mjs.exp
new file mode 100644
index 000000000..60d68c5d3
--- /dev/null
+++ b/tests/ports/webassembly/run_python_async2.mjs.exp
@@ -0,0 +1,8 @@
+_text await start
+_text awaited end
+_Response.text start
+_Response.text end
+OK
+_Response.text start
+_Response.text end
+OK
diff --git a/tests/ports/webassembly/this_behaviour.mjs b/tests/ports/webassembly/this_behaviour.mjs
new file mode 100644
index 000000000..6411b6ce6
--- /dev/null
+++ b/tests/ports/webassembly/this_behaviour.mjs
@@ -0,0 +1,24 @@
+// Test "this" behaviour.
+
+const mp = await (await import(process.argv[2])).loadMicroPython();
+
+// "this" should be undefined.
+globalThis.func0 = function () {
+ console.log("func0", this);
+};
+mp.runPython("import js; js.func0()");
+
+globalThis.func1 = function (a) {
+ console.log("func1", a, this);
+};
+mp.runPython("import js; js.func1(123)");
+
+globalThis.func2 = function (a, b) {
+ console.log("func2", a, b, this);
+};
+mp.runPython("import js; js.func2(123, 456)");
+
+globalThis.func3 = function (a, b, c) {
+ console.log("func3", a, b, c, this);
+};
+mp.runPython("import js; js.func3(123, 456, 789)");
diff --git a/tests/ports/webassembly/this_behaviour.mjs.exp b/tests/ports/webassembly/this_behaviour.mjs.exp
new file mode 100644
index 000000000..4762026ab
--- /dev/null
+++ b/tests/ports/webassembly/this_behaviour.mjs.exp
@@ -0,0 +1,4 @@
+func0 undefined
+func1 123 undefined
+func2 123 456 undefined
+func3 123 456 789 undefined
diff --git a/tests/ports/webassembly/various.js b/tests/ports/webassembly/various.js
new file mode 100644
index 000000000..e2fa9362c
--- /dev/null
+++ b/tests/ports/webassembly/various.js
@@ -0,0 +1,38 @@
+import(process.argv[2]).then((mp) => {
+ mp.loadMicroPython().then((py) => {
+ globalThis.jsadd = (x, y) => {
+ return x + y;
+ };
+ py.runPython("import js; print(js); print(js.jsadd(4, 9))");
+
+ py.runPython(
+ "def set_timeout_callback():\n print('set_timeout_callback')",
+ );
+ py.runPython("import js; js.setTimeout(set_timeout_callback, 100)");
+
+ py.runPython("obj = js.Object(a=1)");
+ console.log("main", py.pyimport("__main__").obj);
+
+ console.log("=======");
+ py.runPython(`
+ from js import Array, Promise, Reflect
+
+ def callback(resolve, reject):
+ resolve('OK1')
+
+ p = Reflect.construct(Promise, Array(callback))
+ p.then(print)
+ `);
+
+ console.log("=======");
+ py.runPython(`
+ from js import Promise
+
+ def callback(resolve, reject):
+ resolve('OK2')
+
+ p = Promise.new(callback)
+ p.then(print)
+ `);
+ });
+});
diff --git a/tests/ports/webassembly/various.js.exp b/tests/ports/webassembly/various.js.exp
new file mode 100644
index 000000000..502ab2ccc
--- /dev/null
+++ b/tests/ports/webassembly/various.js.exp
@@ -0,0 +1,8 @@
+<module 'js'>
+13
+main { a: 1 }
+=======
+=======
+OK1
+OK2
+set_timeout_callback
diff --git a/tests/run-tests.py b/tests/run-tests.py
index 83af61c83..4f55cdd39 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -1142,7 +1142,7 @@ the last matching regex is used:
"ports/qemu-arm",
)
elif args.target == "webassembly":
- test_dirs += ("float",)
+ test_dirs += ("float", "ports/webassembly")
else:
# run tests from these directories
test_dirs = args.test_dirs