diff --git a/tests/js/xhr.simple b/tests/js/xhr.simple
deleted file mode 100644
index 8c61e6dc..00000000
--- a/tests/js/xhr.simple
+++ /dev/null
@@ -1,136 +0,0 @@
-/**
- * @file xhr.simple
- *
- * Simple smoke test which ensures that XMLHttpRequest is initialized, that the
- * constructor works, and that it can send requests and receive responses.
- *
- * @author Caleb Aikens, caleb@distributive.network
- * @date September 2023
- */
-
-const expectedBody = `
-
-
- Example Domain
-
-
-
-
-
-
-
-
-
-
Example Domain
-
This domain is for use in illustrative examples in documents. You may use this
- domain in literature without prior coordination or asking for permission.
-
More information...
-
-
-
-`;
-
-new Promise(function (resolve, reject)
-{
- let xhr = new XMLHttpRequest();
- xhr.open('GET', 'http://www.example.org/');
-
- xhr.onload = function ()
- {
- if (this.status >= 200 && this.status < 300)
- {
- resolve(this.response);
- }
- else
- {
- reject(new Error(JSON.stringify({
- status: this.status,
- statusText: this.statusText
- })));
- }
- };
-
- xhr.onerror = function (ev)
- {
- reject(ev.error);
- };
-
- xhr.send();
-}).then((value) =>
-{
- if (value !== expectedBody)
- {
- console.error('expected ', expectedBody, ' but got ', value);
- throw new Error('Test failed');
- }
- console.log('Test passed');
-}).catch((error) =>
-{
- throw error;
-});
-
-
-new Promise(function (resolve, reject)
-{
- let xhr = new XMLHttpRequest();
- xhr.open('POST', 'http://httpbin.org/post');
-
- xhr.onload = function ()
- {
- if (this.status >= 200 && this.status < 300)
- {
- resolve(this.response);
- }
- else
- {
- reject(new Error(JSON.stringify({
- status: this.status,
- statusText: this.statusText
- })));
- }
- };
-
- xhr.onerror = function (ev)
- {
- reject(ev.error);
- };
-
- xhr.send();
-}).then((value) =>
-{
- value = JSON.parse(value);
- if (!value['headers']['User-Agent'].startsWith('Python/'))
- {
- console.error('expected Python/* User-Agent, but got ', value.headers['User-Agent']);
- }
- console.log('Test passed');
-}).catch((error) =>
-{
- throw error;
-});
\ No newline at end of file
diff --git a/tests/python/test_xhr.py b/tests/python/test_xhr.py
new file mode 100644
index 00000000..990193eb
--- /dev/null
+++ b/tests/python/test_xhr.py
@@ -0,0 +1,97 @@
+from http.server import HTTPServer, BaseHTTPRequestHandler
+import pythonmonkey as pm
+import threading
+import asyncio
+import json
+
+def test_xhr():
+ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
+ def log_request(self, *args) -> None:
+ return
+
+ def do_GET(self):
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(b"get response")
+
+ def do_POST(self):
+ self.send_response(200)
+ self.send_header('Content-Type', 'application/json')
+ self.end_headers()
+ length = int(self.headers.get('Content-Length'))
+ json_string = self.rfile.read(length).decode("utf-8")
+ parameter_dict = json.loads(json_string)
+ parameter_dict["User-Agent"] = self.headers['User-Agent']
+ data = json.dumps(parameter_dict).encode("utf-8")
+ self.wfile.write(data)
+
+ httpd = HTTPServer(('localhost', 4001), TestHTTPRequestHandler)
+ thread = threading.Thread(target = httpd.serve_forever)
+ thread.daemon = True
+ thread.start()
+
+ async def async_fn():
+ assert "get response" == await pm.eval("""
+ new Promise(function (resolve, reject) {
+ let xhr = new XMLHttpRequest();
+ xhr.open('GET', 'http://localhost:4001');
+
+ xhr.onload = function ()
+ {
+ if (this.status >= 200 && this.status < 300)
+ {
+ resolve(this.response);
+ }
+ else
+ {
+ reject(new Error(JSON.stringify({
+ status: this.status,
+ statusText: this.statusText
+ })));
+ }
+ };
+
+ xhr.onerror = function (ev)
+ {
+ reject(ev.error);
+ };
+ xhr.send();
+ });
+ """)
+
+ post_result = await pm.eval("""
+ new Promise(function (resolve, reject)
+ {
+ let xhr = new XMLHttpRequest();
+ xhr.open('POST', 'http://localhost:4001');
+
+ xhr.onload = function ()
+ {
+ if (this.status >= 200 && this.status < 300)
+ {
+ resolve(this.response);
+ }
+ else
+ {
+ reject(new Error(JSON.stringify({
+ status: this.status,
+ statusText: this.statusText
+ })));
+ }
+ };
+
+ xhr.onerror = function (ev)
+ {
+ console.log(ev)
+ reject(ev.error);
+ };
+
+ xhr.send(JSON.stringify({fromPM: "snakesandmonkeys"}));
+ })
+ """)
+
+ result_json = json.loads(post_result)
+ assert result_json["fromPM"] == "snakesandmonkeys"
+ assert result_json["User-Agent"].startswith("Python/")
+ httpd.shutdown()
+ asyncio.run(async_fn())
\ No newline at end of file