Repository: pyscript/pyscript (language: python)

Issue:
Cleanup the ./test/ folder
TL;DR the test folder is a bit of a disorganized mess and it runs integration tests too but it's not even obvious how to add these or change/fix these too.

This might not be our highest priority but imho, the sooner I reorganize that folder, the better it is for ourselves and eventual contributors.

### What browsers are you seeing the problem on? (if applicable)

_No response_

### Console info

_No response_

### Additional Context

_No response_


Discussion / hints:

Fix patch:
diff --git a/pyscript.core/README.md b/pyscript.core/README.md
index 75ef6af2a79..5aa22199f2f 100644
--- a/pyscript.core/README.md
+++ b/pyscript.core/README.md
@@ -12,7 +12,7 @@ Clone this repository then run `npm install` within its folder.
 
 Use `npm run build` to create all artifacts and _dist_ files.
 
-Use `npm run server` to test locally, via the `http://localhost:8080/test/` url, smoke tests or to test manually anything you'd like to check.
+Use `npm run server` to test locally, via the `http://localhost:8080/tests/` url, smoke tests or to test manually anything you'd like to check.
 
 ### Artifacts
 
diff --git a/pyscript.core/package.json b/pyscript.core/package.json
index 9d9adb17fbc..c2832c9b3d5 100644
--- a/pyscript.core/package.json
+++ b/pyscript.core/package.json
@@ -20,15 +20,16 @@
     },
     "scripts": {
         "server": "npx static-handler --coi .",
-        "build": "export ESLINT_USE_FLAT_CONFIG=true;npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && if [ -z \"$NO_MIN\" ]; then eslint src/ && npm run ts && npm run test:mpy; fi",
+        "build": "export ESLINT_USE_FLAT_CONFIG=true;npm run build:3rd-party && npm run build:stdlib && npm run build:plugins && npm run build:core && npm run build:tests-index && if [ -z \"$NO_MIN\" ]; then eslint src/ && npm run ts && npm run test:integration; fi",
         "build:core": "rm -rf dist && rollup --config rollup/core.config.js && cp src/3rd-party/*.css dist/",
         "build:flatted": "node rollup/flatted.cjs",
         "build:plugins": "node rollup/plugins.cjs",
         "build:stdlib": "node rollup/stdlib.cjs",
         "build:3rd-party": "node rollup/3rd-party.cjs",
+        "build:tests-index": "node rollup/build_test_index.cjs",
         "clean:3rd-party": "rm src/3rd-party/*.js && rm src/3rd-party/*.css",
-        "test:mpy": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test --fully-parallel test/mpy.spec.js || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
-        "test:ws": "bun test/ws/index.js & playwright test test/ws.spec.js",
+        "test:integration": "static-handler --coi . 2>/dev/null & SH_PID=$!; EXIT_CODE=0; playwright test --fully-parallel tests/integration.spec.js || EXIT_CODE=$?; kill $SH_PID 2>/dev/null; exit $EXIT_CODE",
+        "test:ws": "bun tests/ws/index.js & playwright test tests/ws.spec.js",
         "dev": "node dev.cjs",
         "release": "npm run build && npm run zip",
         "size": "echo -e \"\\033[1mdist/*.js file size\\033[0m\"; for js in $(ls dist/*.js); do cat $js | brotli > ._; echo -e \"\\033[2m$js:\\033[0m $(du -h --apparent-size ._ | sed -e 's/[[:space:]]*._//')\"; rm ._; done",


Test patch:
diff --git a/pyscript.core/rollup/build_test_index.cjs b/pyscript.core/rollup/build_test_index.cjs
new file mode 100644
index 00000000000..e35cd3aff00
--- /dev/null
+++ b/pyscript.core/rollup/build_test_index.cjs
@@ -0,0 +1,73 @@
+const { join } = require("node:path");
+const { lstatSync, readdirSync, writeFileSync } = require("node:fs");
+
+// folders to not consider while crawling
+const EXCLUDE_DIR = new Set(["ws"]);
+
+const TEST_DIR = join(__dirname, "..", "tests");
+
+const TEST_INDEX = join(TEST_DIR, "index.html");
+
+const crawl = (path, tree = {}) => {
+    for (const file of readdirSync(path)) {
+        const current = join(path, file);
+        if (current === TEST_INDEX) continue;
+        if (lstatSync(current).isDirectory()) {
+            if (EXCLUDE_DIR.has(file)) continue;
+            const sub = {};
+            tree[file] = sub;
+            crawl(current, sub);
+            if (!Reflect.ownKeys(sub).length) {
+                delete tree[file];
+            }
+        } else if (file.endsWith(".html")) {
+            const name = file === "index.html" ? "." : file.slice(0, -5);
+            tree[name] = current.replace(TEST_DIR, "");
+        }
+    }
+    return tree;
+};
+
+const createList = (tree) => {
+    const ul = ["<ul>"];
+    for (const [key, value] of Object.entries(tree)) {
+        ul.push("<li>");
+        if (typeof value === "string") {
+            ul.push(`<a href=".${value}">${key}<small>.html</small></a>`);
+        } else {
+            if ("." in value) {
+                ul.push(`<strong><a href=".${value["."]}">${key}</a></strong>`);
+                delete value["."];
+            } else {
+                ul.push(`<strong><span>${key}</span></strong>`);
+            }
+            if (Reflect.ownKeys(value).length) ul.push(createList(value));
+        }
+        ul.push("</li>");
+    }
+    ul.push("</ul>");
+    return ul.join("");
+};
+
+writeFileSync(
+    TEST_INDEX,
+    `<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <title>PyScript tests</title>
+        <style>
+            body { font-family: sans-serif; }
+            a {
+                display: block;
+                transition: opacity .3s;
+            }
+            a, span { opacity: .7; }
+            a:hover { opacity: 1; }
+        </style>
+    </head>
+    <body>${createList(crawl(TEST_DIR))}</body>
+</html>
+`,
+);
diff --git a/pyscript.core/test/error.js b/pyscript.core/test/error.js
deleted file mode 100644
index 46a99a9dcac..00000000000
--- a/pyscript.core/test/error.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// PyScript Error Plugin
-import { hooks } from '@pyscript/core';
-
-hooks.onBeforeRun.add(function override(pyScript) {
-  // be sure this override happens only once
-  hooks.onBeforeRun.delete(override);
-
-  // trap generic `stderr` to propagate to it regardless
-  const { stderr } = pyScript.io;
-
-  // override it with our own logic
-  pyScript.io.stderr = (...args) => {
-    // grab the message of the first argument (Error)
-    const [ { message } ] = args;
-    // show it
-    notify(message);
-    // still let other plugins or PyScript itself do the rest
-    return stderr(...args);
-  };
-});
-
-// Error hook utilities
-
-// Custom function to show notifications
-function notify(message) {
-  const div = document.createElement('div');
-  div.textContent = message;
-  div.style.cssText = `
-    border: 1px solid red;
-    background: #ffdddd;
-    color: black;
-    font-family: courier, monospace;
-    white-space: pre;
-    overflow-x: auto;
-    padding: 8px;
-    margin-top: 8px;
-  `;
-  document.body.append(div);
-}
diff --git a/pyscript.core/test/pydom.html b/pyscript.core/test/pydom.html
deleted file mode 100644
index 67ef4638d26..00000000000
--- a/pyscript.core/test/pydom.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>PyDom Example</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
-</head>
-<body>
-  <script type="mpy" src="pydom.py"></script>
-
-  <div id="system-info"></div>
-
-  <button id="just-a-button">Click For Time</button>
-  <button id="color-button">Click For Color</button>
-  <button id="color-reset-button">Reset Color</button>
-
-  <div id="result"></div>
-</body>
-</html>
diff --git a/pyscript.core/test/pydom.py b/pyscript.core/test/pydom.py
deleted file mode 100644
index 3f579e8edb7..00000000000
--- a/pyscript.core/test/pydom.py
+++ /dev/null
@@ -1,33 +0,0 @@
-import random
-import sys
-import time
-from datetime import datetime as dt
-
-from pyscript import display, when
-from pyscript.web import dom
-
-display(sys.version, target="system-info")
-
-
-@when("click", "#just-a-button")
-def on_click():
-    try:
-        timenow = dt.now()
-    except NotImplementedError:
-        # In this case we assume it's not implemented because we are using MycroPython
-        tnow = time.localtime()
-        tstr = "{:02d}/{:02d}/{:04d} {:02d}:{:02d}:{:02d}"
-        timenow = tstr.format(tnow[2], tnow[1], tnow[0], *tnow[2:])
-
-    display(f"Hello from PyScript, time is: {timenow}", append=False, target="#result")
-
-
-@when("click", "#color-button")
-def on_color_click(event):
-    btn = dom["#result"]
-    btn.style["background-color"] = f"#{random.randrange(0x1000000):06x}"
-
-
-@when("click", "#color-reset-button")
-def reset_color(*args, **kwargs):
-    dom["#result"].style["background-color"] = "white"
diff --git a/pyscript.core/test/pydom_mp.html b/pyscript.core/test/pydom_mp.html
deleted file mode 100644
index ef1a883b7be..00000000000
--- a/pyscript.core/test/pydom_mp.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <title>PyDom Example (MicroPython)</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
-</head>
-<body>
-  <script type="mpy" src="pydom.py"></script>
-
-  <div id="system-info"></div>
-
-  <button id="just-a-button">Click For Time</button>
-  <button id="color-button">Click For Color</button>
-  <button id="color-reset-button">Reset Color</button>
-
-  <div id="result"></div>
-</body>
-</html>
diff --git a/pyscript.core/test/test.html b/pyscript.core/test/test.html
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/pyscript.core/test/config/ambiguous-config.html b/pyscript.core/tests/config/ambiguous-config.html
similarity index 100%
rename from pyscript.core/test/config/ambiguous-config.html
rename to pyscript.core/tests/config/ambiguous-config.html
diff --git a/pyscript.core/test/config/index.html b/pyscript.core/tests/config/index.html
similarity index 100%
rename from pyscript.core/test/config/index.html
rename to pyscript.core/tests/config/index.html
diff --git a/pyscript.core/test/config/same-config.html b/pyscript.core/tests/config/same-config.html
similarity index 100%
rename from pyscript.core/test/config/same-config.html
rename to pyscript.core/tests/config/same-config.html
diff --git a/pyscript.core/test/config/too-many-config.html b/pyscript.core/tests/config/too-many-config.html
similarity index 100%
rename from pyscript.core/test/config/too-many-config.html
rename to pyscript.core/tests/config/too-many-config.html
diff --git a/pyscript.core/test/config/too-many-py-config.html b/pyscript.core/tests/config/too-many-py-config.html
similarity index 100%
rename from pyscript.core/test/config/too-many-py-config.html
rename to pyscript.core/tests/config/too-many-py-config.html
diff --git a/pyscript.core/tests/index.html b/pyscript.core/tests/index.html
new file mode 100644
index 00000000000..14b7940a3e2
--- /dev/null
+++ b/pyscript.core/tests/index.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <title>PyScript tests</title>
+        <style>
+            body { font-family: sans-serif; }
+            a {
+                display: block;
+                transition: opacity .3s;
+            }
+            a, span { opacity: .7; }
+            a:hover { opacity: 1; }
+        </style>
+    </head>
+    <body><ul><li><strong><a href="./config/index.html">config</a></strong><ul><li><a href="./config/ambiguous-config.html">ambiguous-config<small>.html</small></a></li><li><a href="./config/same-config.html">same-config<small>.html</small></a></li><li><a href="./config/too-many-config.html">too-many-config<small>.html</small></a></li><li><a href="./config/too-many-py-config.html">too-many-py-config<small>.html</small></a></li></ul></li><li><strong><a href="./issue-7015/index.html">issue-7015</a></strong></li><li><strong><span>js-integration</span></strong><ul><li><a href="./js-integration/config-url.html">config-url<small>.html</small></a></li><li><strong><a href="./js-integration/fetch/index.html">fetch</a></strong></li><li><a href="./js-integration/ffi.html">ffi<small>.html</small></a></li><li><a href="./js-integration/hooks.html">hooks<small>.html</small></a></li><li><strong><a href="./js-integration/issue-2093/index.html">issue-2093</a></strong></li><li><a href="./js-integration/js_modules.html">js_modules<small>.html</small></a></li><li><a href="./js-integration/mpy.html">mpy<small>.html</small></a></li><li><a href="./js-integration/py-terminal-main.html">py-terminal-main<small>.html</small></a></li><li><a href="./js-integration/py-terminal-worker.html">py-terminal-worker<small>.html</small></a></li><li><a href="./js-integration/py-terminal.html">py-terminal<small>.html</small></a></li><li><a href="./js-integration/py-terminals.html">py-terminals<small>.html</small></a></li><li><strong><a href="./js-integration/pyscript_dom/index.html">pyscript_dom</a></strong></li><li><a href="./js-integration/storage.html">storage<small>.html</small></a></li><li><strong><a href="./js-integration/workers/index.html">workers</a></strong><ul><li><a href="./js-integration/workers/named.html">named<small>.html</small></a></li></ul></li></ul></li><li><strong><a href="./manual/index.html">manual</a></strong><ul><li><a href="./manual/all-done.html">all-done<small>.html</small></a></li><li><a href="./manual/async.html">async<small>.html</small></a></li><li><a href="./manual/camera.html">camera<small>.html</small></a></li><li><a href="./manual/click.html">click<small>.html</small></a></li><li><a href="./manual/code-a-part.html">code-a-part<small>.html</small></a></li><li><a href="./manual/combo.html">combo<small>.html</small></a></li><li><a href="./manual/config.html">config<small>.html</small></a></li><li><a href="./manual/create-element.html">create-element<small>.html</small></a></li><li><a href="./manual/dialog.html">dialog<small>.html</small></a></li><li><a href="./manual/display.html">display<small>.html</small></a></li><li><a href="./manual/error.html">error<small>.html</small></a></li><li><a href="./manual/html-decode.html">html-decode<small>.html</small></a></li><li><a href="./manual/input.html">input<small>.html</small></a></li><li><a href="./manual/multi.html">multi<small>.html</small></a></li><li><a href="./manual/multiple-editors.html">multiple-editors<small>.html</small></a></li><li><a href="./manual/no-error.html">no-error<small>.html</small></a></li><li><a href="./manual/py-editor.html">py-editor<small>.html</small></a></li><li><a href="./manual/py_modules.html">py_modules<small>.html</small></a></li><li><a href="./manual/split-config.html">split-config<small>.html</small></a></li><li><a href="./manual/target.html">target<small>.html</small></a></li><li><a href="./manual/test_display_HTML.html">test_display_HTML<small>.html</small></a></li><li><a href="./manual/test_when.html">test_when<small>.html</small></a></li><li><a href="./manual/worker.html">worker<small>.html</small></a></li></ul></li><li><strong><a href="./no_sab/index.html">no_sab</a></strong></li><li><strong><a href="./piratical/index.html">piratical</a></strong></li><li><strong><a href="./py-editor/index.html">py-editor</a></strong><ul><li><a href="./py-editor/issue-2056.html">issue-2056<small>.html</small></a></li><li><a href="./py-editor/service-worker.html">service-worker<small>.html</small></a></li></ul></li><li><strong><a href="./py-terminals/index.html">py-terminals</a></strong><ul><li><a href="./py-terminals/no-repl.html">no-repl<small>.html</small></a></li><li><a href="./py-terminals/repl.html">repl<small>.html</small></a></li></ul></li><li><strong><a href="./service-worker/index.html">service-worker</a></strong></li><li><strong><a href="./ui/index.html">ui</a></strong><ul><li><a href="./ui/gallery.html">gallery<small>.html</small></a></li></ul></li></ul></body>
+</html>
diff --git a/pyscript.core/test/mpy.spec.js b/pyscript.core/tests/integration.spec.js
similarity index 70%
rename from pyscript.core/test/mpy.spec.js
rename to pyscript.core/tests/integration.spec.js
index ba1556ea503..b062777eb2e 100644
--- a/pyscript.core/test/mpy.spec.js
+++ b/pyscript.core/tests/integration.spec.js
@@ -1,7 +1,7 @@
 import { test, expect } from '@playwright/test';
 
 test('MicroPython display', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/mpy.html');
+  await page.goto('http://localhost:8080/tests/js-integration/mpy.html');
   await page.waitForSelector('html.done.worker');
   const body = await page.evaluate(() => document.body.innerText);
   await expect(body.trim()).toBe([
@@ -18,7 +18,7 @@ test('MicroPython hooks', async ({ page }) => {
     if (!text.startsWith('['))
       logs.push(text);
   });
-  await page.goto('http://localhost:8080/test/hooks.html');
+  await page.goto('http://localhost:8080/tests/js-integration/hooks.html');
   await page.waitForSelector('html.done.worker');
   await expect(logs.join('\n')).toBe([
     'main onReady',
@@ -43,7 +43,7 @@ test('MicroPython + Pyodide js_modules', async ({ page }) => {
     if (!text.startsWith('['))
       logs.push(text);
   });
-  await page.goto('http://localhost:8080/test/js_modules.html');
+  await page.goto('http://localhost:8080/tests/js-integration/js_modules.html');
   await page.waitForSelector('html.done');
   await expect(logs.length).toBe(6);
   await expect(logs[0]).toBe(logs[1]);
@@ -53,53 +53,47 @@ test('MicroPython + Pyodide js_modules', async ({ page }) => {
 });
 
 test('MicroPython + configURL', async ({ page }) => {
-  const logs = [];
-  page.on('console', msg => {
-    const text = msg.text();
-    if (!text.startsWith('['))
-      logs.push(text);
-  });
-  await page.goto('http://localhost:8080/test/config-url.html');
+  await page.goto('http://localhost:8080/tests/js-integration/config-url.html');
   await page.waitForSelector('html.main.worker');
 });
 
 test('Pyodide + terminal on Main', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/py-terminal-main.html');
+  await page.goto('http://localhost:8080/tests/js-integration/py-terminal-main.html');
   await page.waitForSelector('html.ok');
 });
 
 
 test('Pyodide + terminal on Worker', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/py-terminal-worker.html');
+  await page.goto('http://localhost:8080/tests/js-integration/py-terminal-worker.html');
   await page.waitForSelector('html.ok');
 });
 
 test('Pyodide + multiple terminals via Worker', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/py-terminals.html');
+  await page.goto('http://localhost:8080/tests/js-integration/py-terminals.html');
   await page.waitForSelector('html.first.second');
 });
 
 test('MicroPython + Pyodide fetch', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/fetch.html');
+  await page.goto('http://localhost:8080/tests/js-integration/fetch/index.html');
   await page.waitForSelector('html.mpy.py');
 });
 
 test('MicroPython + Pyodide ffi', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/ffi.html');
+  await page.goto('http://localhost:8080/tests/js-integration/ffi.html');
   await page.waitForSelector('html.mpy.py');
 });
 
 test('MicroPython + Storage', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/storage.html');
+  await page.goto('http://localhost:8080/tests/js-integration/storage.html');
   await page.waitForSelector('html.ok');
 });
 
 test('MicroPython + workers', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/workers/index.html');
+  await page.goto('http://localhost:8080/tests/js-integration/workers/index.html');
   await page.waitForSelector('html.mpy.py');
 });
 
 test('MicroPython Editor setup error', async ({ page }) => {
-  await page.goto('http://localhost:8080/test/issue-2093/index.html');
+  await page.goto('http://localhost:8080/tests/js-integration/issue-2093/index.html');
   await page.waitForSelector('html.errored');
 });
diff --git a/pyscript.core/tests/integration/support.py b/pyscript.core/tests/integration/support.py
index b9aadf835e9..81b7ae950dd 100644
--- a/pyscript.core/tests/integration/support.py
+++ b/pyscript.core/tests/integration/support.py
@@ -17,7 +17,7 @@
 
 ROOT = py.path.local(__file__).dirpath("..", "..", "..")
 BUILD = ROOT.join("pyscript.core").join("dist")
-TEST = ROOT.join("pyscript.core").join("test")
+TEST = ROOT.join("pyscript.core").join("tests")
 
 
 def params_with_marks(params):
@@ -212,7 +212,7 @@ def init(self, request, tmpdir, logger, page, execution_thread):
         tmpdir.join("dist").mksymlinkto(BUILD)
         # create a symlink to TEST inside tmpdir so we can run tests in that
         # manual test folder
-        tmpdir.join("test").mksymlinkto(TEST)
+        tmpdir.join("tests").mksymlinkto(TEST)
 
         # create a symlink to the favicon, so that we can use it in the HTML
         self.tmpdir.chdir()
diff --git a/pyscript.core/tests/integration/test_integration.py b/pyscript.core/tests/integration/test_integration.py
index 4b9b0282679..aa6646a9586 100644
--- a/pyscript.core/tests/integration/test_integration.py
+++ b/pyscript.core/tests/integration/test_integration.py
@@ -5,7 +5,6 @@
 class TestSmokeTests(PyScriptTest):
     """
     Each example requires the same three tests:
-
         - Test that the initial markup loads properly (currently done by
           testing the <title> tag's content)
         - Testing that pyscript is loading properly
@@ -14,7 +13,7 @@ class TestSmokeTests(PyScriptTest):
 
     def test_pydom(self):
         # Test the full pydom test suite by running it in the browser
-        self.goto("test/pyscript_dom/index.html?-v&-s")
+        self.goto("tests/pyscript_dom/index.html?-v&-s")
         assert self.page.title() == "PyDom Test Suite"
 
         # wait for the test suite to finish
diff --git a/pyscript.core/test/issue-7015/config.toml b/pyscript.core/tests/issue-7015/config.toml
similarity index 100%
rename from pyscript.core/test/issue-7015/config.toml
rename to pyscript.core/tests/issue-7015/config.toml
diff --git a/pyscript.core/test/issue-7015/index.html b/pyscript.core/tests/issue-7015/index.html
similarity index 100%
rename from pyscript.core/test/issue-7015/index.html
rename to pyscript.core/tests/issue-7015/index.html
diff --git a/pyscript.core/test/issue-7015/main.py b/pyscript.core/tests/issue-7015/main.py
similarity index 100%
rename from pyscript.core/test/issue-7015/main.py
rename to pyscript.core/tests/issue-7015/main.py
diff --git a/pyscript.core/test/config-url.html b/pyscript.core/tests/js-integration/config-url.html
similarity index 84%
rename from pyscript.core/test/config-url.html
rename to pyscript.core/tests/js-integration/config-url.html
index 566de6145da..9f28969dd57 100644
--- a/pyscript.core/test/config-url.html
+++ b/pyscript.core/tests/js-integration/config-url.html
@@ -4,8 +4,8 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Next Plugin</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
   <mpy-config src="config-url/config.json"></mpy-config>
   <script type="mpy">
     from pyscript import config
diff --git a/pyscript.core/test/config-url/config.json b/pyscript.core/tests/js-integration/config-url/config.json
similarity index 100%
rename from pyscript.core/test/config-url/config.json
rename to pyscript.core/tests/js-integration/config-url/config.json
diff --git a/pyscript.core/test/config-url/src/test.py b/pyscript.core/tests/js-integration/config-url/src/test.py
similarity index 100%
rename from pyscript.core/test/config-url/src/test.py
rename to pyscript.core/tests/js-integration/config-url/src/test.py
diff --git a/pyscript.core/test/a.py b/pyscript.core/tests/js-integration/fetch/a.py
similarity index 100%
rename from pyscript.core/test/a.py
rename to pyscript.core/tests/js-integration/fetch/a.py
diff --git a/pyscript.core/test/config.json b/pyscript.core/tests/js-integration/fetch/config.json
similarity index 100%
rename from pyscript.core/test/config.json
rename to pyscript.core/tests/js-integration/fetch/config.json
diff --git a/pyscript.core/test/fetch.html b/pyscript.core/tests/js-integration/fetch/index.html
similarity index 96%
rename from pyscript.core/test/fetch.html
rename to pyscript.core/tests/js-integration/fetch/index.html
index 35f8fb79a70..701a6d34f20 100644
--- a/pyscript.core/test/fetch.html
+++ b/pyscript.core/tests/js-integration/fetch/index.html
@@ -3,7 +3,7 @@
     <head>
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-        <link rel="stylesheet" href="../dist/core.css">
+        <link rel="stylesheet" href="../../../dist/core.css">
     </head>
     <body>
       <script type="module">
@@ -18,7 +18,7 @@
             document.createElement('script'),
             {
               type: 'module',
-              src: '../dist/core.js'
+              src: '../../../dist/core.js'
             }
           )
         );
diff --git a/pyscript.core/test/ffi.html b/pyscript.core/tests/js-integration/ffi.html
similarity index 84%
rename from pyscript.core/test/ffi.html
rename to pyscript.core/tests/js-integration/ffi.html
index 1c29ccb82c4..16f7ece424a 100644
--- a/pyscript.core/test/ffi.html
+++ b/pyscript.core/tests/js-integration/ffi.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript FFI</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
       <script type="mpy">
diff --git a/pyscript.core/test/hooks.html b/pyscript.core/tests/js-integration/hooks.html
similarity index 94%
rename from pyscript.core/test/hooks.html
rename to pyscript.core/tests/js-integration/hooks.html
index ed43118f0ba..857ed4f5c22 100644
--- a/pyscript.core/test/hooks.html
+++ b/pyscript.core/tests/js-integration/hooks.html
@@ -4,13 +4,13 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Next Plugin Bug?</title>
-  <link rel="stylesheet" href="../dist/core.css">
+  <link rel="stylesheet" href="../../dist/core.css">
   <script type="module">
     addEventListener('mpy:done', () => {
       document.documentElement.classList.add('done');
     });
 
-    import { hooks } from "../dist/core.js";
+    import { hooks } from "../../dist/core.js";
 
     // Main
     hooks.main.onReady.add((wrap, element) => {
diff --git a/pyscript.core/test/issue-2093/error.js b/pyscript.core/tests/js-integration/issue-2093/error.js
similarity index 100%
rename from pyscript.core/test/issue-2093/error.js
rename to pyscript.core/tests/js-integration/issue-2093/error.js
diff --git a/pyscript.core/test/issue-2093/index.html b/pyscript.core/tests/js-integration/issue-2093/index.html
similarity index 74%
rename from pyscript.core/test/issue-2093/index.html
rename to pyscript.core/tests/js-integration/issue-2093/index.html
index 293078fb1c7..ff6720c1727 100644
--- a/pyscript.core/test/issue-2093/index.html
+++ b/pyscript.core/tests/js-integration/issue-2093/index.html
@@ -3,9 +3,9 @@
     <head>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-        <link rel="stylesheet" href="../../dist/core.css">
+        <link rel="stylesheet" href="../../../dist/core.css">
         <script type="module" src="./error.js"></script>
-        <script type="module" src="../../dist/core.js"></script>
+        <script type="module" src="../../../dist/core.js"></script>
     </head>
     <body>
       <script type="mpy-editor" setup>
diff --git a/pyscript.core/test/js_modules.html b/pyscript.core/tests/js-integration/js_modules.html
similarity index 90%
rename from pyscript.core/test/js_modules.html
rename to pyscript.core/tests/js-integration/js_modules.html
index b012c51660d..bb0c5e90557 100644
--- a/pyscript.core/test/js_modules.html
+++ b/pyscript.core/tests/js-integration/js_modules.html
@@ -3,8 +3,8 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <link rel="stylesheet" href="../dist/core.css">
-    <script type="module" src="../dist/core.js"></script>
+    <link rel="stylesheet" href="../../dist/core.css">
+    <script type="module" src="../../dist/core.js"></script>
 </head>
 <body>
     <mpy-config>
diff --git a/pyscript.core/test/js_modules.js b/pyscript.core/tests/js-integration/js_modules.js
similarity index 100%
rename from pyscript.core/test/js_modules.js
rename to pyscript.core/tests/js-integration/js_modules.js
diff --git a/pyscript.core/test/mpy.html b/pyscript.core/tests/js-integration/mpy.html
similarity index 88%
rename from pyscript.core/test/mpy.html
rename to pyscript.core/tests/js-integration/mpy.html
index 4f0ae4b69e4..1d928af0e4b 100644
--- a/pyscript.core/test/mpy.html
+++ b/pyscript.core/tests/js-integration/mpy.html
@@ -9,8 +9,8 @@
                 document.documentElement.classList.add('done');
             });
         </script>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <script type="mpy">
diff --git a/pyscript.core/test/py-terminal-main.html b/pyscript.core/tests/js-integration/py-terminal-main.html
similarity index 73%
rename from pyscript.core/test/py-terminal-main.html
rename to pyscript.core/tests/js-integration/py-terminal-main.html
index f7fdcf9e265..005ee224313 100644
--- a/pyscript.core/test/py-terminal-main.html
+++ b/pyscript.core/tests/js-integration/py-terminal-main.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyTerminal Main</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
         <style>.xterm { padding: .5rem; }</style>
     </head>
     <body>
diff --git a/pyscript.core/test/py-terminal-worker.html b/pyscript.core/tests/js-integration/py-terminal-worker.html
similarity index 77%
rename from pyscript.core/test/py-terminal-worker.html
rename to pyscript.core/tests/js-integration/py-terminal-worker.html
index 3201c99ee12..ae791faec19 100644
--- a/pyscript.core/test/py-terminal-worker.html
+++ b/pyscript.core/tests/js-integration/py-terminal-worker.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyTerminal Main</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
         <style>.xterm { padding: .5rem; }</style>
     </head>
     <body>
diff --git a/pyscript.core/test/py-terminal.html b/pyscript.core/tests/js-integration/py-terminal.html
similarity index 77%
rename from pyscript.core/test/py-terminal.html
rename to pyscript.core/tests/js-integration/py-terminal.html
index d4ab3537f8b..701d36217a8 100644
--- a/pyscript.core/test/py-terminal.html
+++ b/pyscript.core/tests/js-integration/py-terminal.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyTerminal</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
         <style>.xterm { padding: .5rem; }</style>
     </head>
     <body>
diff --git a/pyscript.core/test/py-terminals.html b/pyscript.core/tests/js-integration/py-terminals.html
similarity index 85%
rename from pyscript.core/test/py-terminals.html
rename to pyscript.core/tests/js-integration/py-terminals.html
index a846ea7c8ca..1e0c0132911 100644
--- a/pyscript.core/test/py-terminals.html
+++ b/pyscript.core/tests/js-integration/py-terminals.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyTerminal Main</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
         <style>.xterm { padding: .5rem; }</style>
     </head>
     <body>
diff --git a/pyscript.core/test/storage.html b/pyscript.core/tests/js-integration/storage.html
similarity index 91%
rename from pyscript.core/test/storage.html
rename to pyscript.core/tests/js-integration/storage.html
index de602e3fb39..3aea830bf70 100644
--- a/pyscript.core/test/storage.html
+++ b/pyscript.core/tests/js-integration/storage.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>@pyscript/core storage</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <script type="mpy" async>
diff --git a/pyscript.core/test/terminal.py b/pyscript.core/tests/js-integration/terminal.py
similarity index 100%
rename from pyscript.core/test/terminal.py
rename to pyscript.core/tests/js-integration/terminal.py
diff --git a/pyscript.core/test/workers/config.toml b/pyscript.core/tests/js-integration/workers/config.toml
similarity index 100%
rename from pyscript.core/test/workers/config.toml
rename to pyscript.core/tests/js-integration/workers/config.toml
diff --git a/pyscript.core/test/workers/index.html b/pyscript.core/tests/js-integration/workers/index.html
similarity index 92%
rename from pyscript.core/test/workers/index.html
rename to pyscript.core/tests/js-integration/workers/index.html
index b0ab9a7266e..99b61efcc0d 100644
--- a/pyscript.core/test/workers/index.html
+++ b/pyscript.core/tests/js-integration/workers/index.html
@@ -3,7 +3,7 @@
     <head>
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width,initial-scale=1.0">
-        <script type="module" src="../../dist/core.js"></script>
+        <script type="module" src="../../../dist/core.js"></script>
     </head>
     <body>
         <script type="mpy" async>
diff --git a/pyscript.core/test/workers/named.html b/pyscript.core/tests/js-integration/workers/named.html
similarity index 91%
rename from pyscript.core/test/workers/named.html
rename to pyscript.core/tests/js-integration/workers/named.html
index b833d1fdcd4..6079becc267 100644
--- a/pyscript.core/test/workers/named.html
+++ b/pyscript.core/tests/js-integration/workers/named.html
@@ -4,7 +4,7 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width,initial-scale=1.0" />
         <title>named workers</title>
-        <script type="module" src="../../dist/core.js"></script>
+        <script type="module" src="../../../dist/core.js"></script>
     </head>
     <body>
         <script type="mpy" async>
diff --git a/pyscript.core/test/workers/test.py b/pyscript.core/tests/js-integration/workers/test.py
similarity index 100%
rename from pyscript.core/test/workers/test.py
rename to pyscript.core/tests/js-integration/workers/test.py
diff --git a/pyscript.core/test/workers/worker.py b/pyscript.core/tests/js-integration/workers/worker.py
similarity index 100%
rename from pyscript.core/test/workers/worker.py
rename to pyscript.core/tests/js-integration/workers/worker.py
diff --git a/pyscript.core/tests/manual/a.py b/pyscript.core/tests/manual/a.py
new file mode 100644
index 00000000000..e1b904b7caa
--- /dev/null
+++ b/pyscript.core/tests/manual/a.py
@@ -0,0 +1,1 @@
+print("a")
diff --git a/pyscript.core/test/all-done.html b/pyscript.core/tests/manual/all-done.html
similarity index 88%
rename from pyscript.core/test/all-done.html
rename to pyscript.core/tests/manual/all-done.html
index a0848175535..031f972f870 100644
--- a/pyscript.core/test/all-done.html
+++ b/pyscript.core/tests/manual/all-done.html
@@ -3,9 +3,9 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <link rel="stylesheet" href="../dist/core.css">
+  <link rel="stylesheet" href="../../dist/core.css">
   <script type="module">
-    import '../dist/core.js';
+    import '../../dist/core.js';
 
     document.body.append('loading ...', document.createElement('br'));
 
diff --git a/pyscript.core/test/async.html b/pyscript.core/tests/manual/async.html
similarity index 78%
rename from pyscript.core/test/async.html
rename to pyscript.core/tests/manual/async.html
index f54e9445c70..67dbbdd543d 100644
--- a/pyscript.core/test/async.html
+++ b/pyscript.core/tests/manual/async.html
@@ -1,8 +1,8 @@
 <!doctype html>
 <html lang="en">
     <head>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <py-script>
diff --git a/pyscript.core/test/bad.toml b/pyscript.core/tests/manual/bad.toml
similarity index 100%
rename from pyscript.core/test/bad.toml
rename to pyscript.core/tests/manual/bad.toml
diff --git a/pyscript.core/test/camera.html b/pyscript.core/tests/manual/camera.html
similarity index 82%
rename from pyscript.core/test/camera.html
rename to pyscript.core/tests/manual/camera.html
index 915d46b44b5..3c6a7f5933e 100644
--- a/pyscript.core/test/camera.html
+++ b/pyscript.core/tests/manual/camera.html
@@ -4,8 +4,8 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Media Example</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
 </head>
 <body>
   <script type="py" src="camera.py" async></script>
diff --git a/pyscript.core/test/camera.py b/pyscript.core/tests/manual/camera.py
similarity index 100%
rename from pyscript.core/test/camera.py
rename to pyscript.core/tests/manual/camera.py
diff --git a/pyscript.core/test/click.html b/pyscript.core/tests/manual/click.html
similarity index 86%
rename from pyscript.core/test/click.html
rename to pyscript.core/tests/manual/click.html
index 576c87b9c7a..378e9f99a4c 100644
--- a/pyscript.core/test/click.html
+++ b/pyscript.core/tests/manual/click.html
@@ -4,8 +4,8 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Next Plugin Bug?</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
 </head>
 <body>
   <script type="py">
diff --git a/pyscript.core/test/code-a-part.html b/pyscript.core/tests/manual/code-a-part.html
similarity index 80%
rename from pyscript.core/test/code-a-part.html
rename to pyscript.core/tests/manual/code-a-part.html
index 2928f29df4b..fcb3f40e543 100644
--- a/pyscript.core/test/code-a-part.html
+++ b/pyscript.core/tests/manual/code-a-part.html
@@ -3,9 +3,9 @@
     <head>
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-        <link rel="stylesheet" href="../dist/core.css">
+        <link rel="stylesheet" href="../../dist/core.css">
         <script type="module">
-            import { hooks } from "../dist/core.js";
+            import { hooks } from "../../dist/core.js";
             hooks.main.codeBeforeRun.add('print(0)');
             hooks.main.codeAfterRun.add('print(2)');
         </script>
diff --git a/pyscript.core/test/combo.html b/pyscript.core/tests/manual/combo.html
similarity index 69%
rename from pyscript.core/test/combo.html
rename to pyscript.core/tests/manual/combo.html
index fc66a9f610e..aca99474918 100644
--- a/pyscript.core/test/combo.html
+++ b/pyscript.core/tests/manual/combo.html
@@ -4,11 +4,11 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript Error</title>
-        <script type="module" src="../dist/core.js"></script>
-        <link rel="stylesheet" href="../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
         <py-config>
           [[fetch]]
-          files = ["a.py"]
+          files = ["./a.py"]
         </py-config>
         <script type="py" worker>
           import a
diff --git a/pyscript.core/test/config.html b/pyscript.core/tests/manual/config.html
similarity index 67%
rename from pyscript.core/test/config.html
rename to pyscript.core/tests/manual/config.html
index e9f0af029d6..e40be7e52e4 100644
--- a/pyscript.core/test/config.html
+++ b/pyscript.core/tests/manual/config.html
@@ -4,8 +4,8 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Next Plugin</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
   <py-config>
     files = [
   </py-config>
diff --git a/pyscript.core/test/create-element.html b/pyscript.core/tests/manual/create-element.html
similarity index 86%
rename from pyscript.core/test/create-element.html
rename to pyscript.core/tests/manual/create-element.html
index f85436066ce..0dcad343c91 100644
--- a/pyscript.core/test/create-element.html
+++ b/pyscript.core/tests/manual/create-element.html
@@ -1,8 +1,8 @@
 <!doctype html>
 <html>
   <head>
-    <link rel="stylesheet" href="../dist/core.css">
-    <script type="module" src="../dist/core.js"></script>
+    <link rel="stylesheet" href="../../dist/core.css">
+    <script type="module" src="../../dist/core.js"></script>
     <script type="module">
       customElements.whenDefined('py-script').then(PyScript => {
         const textContent = `
diff --git a/pyscript.core/test/dialog.html b/pyscript.core/tests/manual/dialog.html
similarity index 85%
rename from pyscript.core/test/dialog.html
rename to pyscript.core/tests/manual/dialog.html
index 05c9736a2d3..649309dae03 100644
--- a/pyscript.core/test/dialog.html
+++ b/pyscript.core/tests/manual/dialog.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript Next</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
         <script type="module">
           const loader = document.querySelector('#loader');
           loader.showModal();
diff --git a/pyscript.core/test/display.html b/pyscript.core/tests/manual/display.html
similarity index 85%
rename from pyscript.core/test/display.html
rename to pyscript.core/tests/manual/display.html
index f7ca53c7221..8b1ced46d45 100644
--- a/pyscript.core/test/display.html
+++ b/pyscript.core/tests/manual/display.html
@@ -7,8 +7,8 @@
         <script>
             addEventListener("py:all-done", ({ type }) => console.log(type));
         </script>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
       <script type="py" worker async>
diff --git a/pyscript.core/test/error.html b/pyscript.core/tests/manual/error.html
similarity index 80%
rename from pyscript.core/test/error.html
rename to pyscript.core/tests/manual/error.html
index 3b6850aa54a..f888ed28b65 100644
--- a/pyscript.core/test/error.html
+++ b/pyscript.core/tests/manual/error.html
@@ -4,8 +4,8 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Next Plugin</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
   <script type="py">
     print(1, 2, 3)
     first()
diff --git a/pyscript.core/test/html-decode.html b/pyscript.core/tests/manual/html-decode.html
similarity index 88%
rename from pyscript.core/test/html-decode.html
rename to pyscript.core/tests/manual/html-decode.html
index 471ec382c04..1db840c4696 100644
--- a/pyscript.core/test/html-decode.html
+++ b/pyscript.core/tests/manual/html-decode.html
@@ -1,8 +1,8 @@
 <!doctype html>
 <html>
   <head>
-    <link rel="stylesheet" href="../dist/core.css">
-    <script type="module" src="../dist/core.js"></script>
+    <link rel="stylesheet" href="../../dist/core.css">
+    <script type="module" src="../../dist/core.js"></script>
   </head>
   <body>
     <body>
diff --git a/pyscript.core/test/index.html b/pyscript.core/tests/manual/index.html
similarity index 79%
rename from pyscript.core/test/index.html
rename to pyscript.core/tests/manual/index.html
index 7c537c014ca..f243947e5c3 100644
--- a/pyscript.core/test/index.html
+++ b/pyscript.core/tests/manual/index.html
@@ -7,8 +7,8 @@
         <script>
             addEventListener("py:ready", console.log);
         </script>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <script type="py">
diff --git a/pyscript.core/test/input.html b/pyscript.core/tests/manual/input.html
similarity index 79%
rename from pyscript.core/test/input.html
rename to pyscript.core/tests/manual/input.html
index b0fd1dcb669..2cbb4496a2a 100644
--- a/pyscript.core/test/input.html
+++ b/pyscript.core/tests/manual/input.html
@@ -7,8 +7,8 @@
         <script>
             addEventListener("py:ready", console.log);
         </script>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <py-script>
diff --git a/pyscript.core/test/multi.html b/pyscript.core/tests/manual/multi.html
similarity index 87%
rename from pyscript.core/test/multi.html
rename to pyscript.core/tests/manual/multi.html
index ce3e62b2ddf..e85ba5262cf 100644
--- a/pyscript.core/test/multi.html
+++ b/pyscript.core/tests/manual/multi.html
@@ -3,7 +3,7 @@
   <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <script type="module" src="../dist/core.js"></script>
+    <script type="module" src="../../dist/core.js"></script>
   </head>
   <body>
     <script type="mpy">
diff --git a/pyscript.core/test/multiple-editors.html b/pyscript.core/tests/manual/multiple-editors.html
similarity index 81%
rename from pyscript.core/test/multiple-editors.html
rename to pyscript.core/tests/manual/multiple-editors.html
index 7ddf44a9ff0..c2b3d257038 100644
--- a/pyscript.core/test/multiple-editors.html
+++ b/pyscript.core/tests/manual/multiple-editors.html
@@ -4,8 +4,8 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width,initial-scale=1">
   <title>PyScript Test</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
 </head>
 <body>
   <script type="py-editor">
diff --git a/pyscript.core/test/no-error.html b/pyscript.core/tests/manual/no-error.html
similarity index 79%
rename from pyscript.core/test/no-error.html
rename to pyscript.core/tests/manual/no-error.html
index 92e06f13c29..858f7a6689f 100644
--- a/pyscript.core/test/no-error.html
+++ b/pyscript.core/tests/manual/no-error.html
@@ -4,8 +4,8 @@
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>PyScript Next No Plugin</title>
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
   <py-config>plugins = ['!error']</py-config>
   <script type="py">
     print(1, 2, 3)
diff --git a/pyscript.core/test/py-editor.html b/pyscript.core/tests/manual/py-editor.html
similarity index 86%
rename from pyscript.core/test/py-editor.html
rename to pyscript.core/tests/manual/py-editor.html
index ad8f8272c67..dc402e4272b 100644
--- a/pyscript.core/test/py-editor.html
+++ b/pyscript.core/tests/manual/py-editor.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyTerminal</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <script type="py-editor">
diff --git a/pyscript.core/test/py_modules.html b/pyscript.core/tests/manual/py_modules.html
similarity index 85%
rename from pyscript.core/test/py_modules.html
rename to pyscript.core/tests/manual/py_modules.html
index 58c837149a1..bb69b06c3ca 100644
--- a/pyscript.core/test/py_modules.html
+++ b/pyscript.core/tests/manual/py_modules.html
@@ -3,8 +3,8 @@
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <link rel="stylesheet" href="../dist/core.css">
-  <script type="module" src="../dist/core.js"></script>
+  <link rel="stylesheet" href="../../dist/core.css">
+  <script type="module" src="../../dist/core.js"></script>
 </head>
 <body>
   <script type="py" async>
diff --git a/pyscript.core/test/split-config.html b/pyscript.core/tests/manual/split-config.html
similarity index 75%
rename from pyscript.core/test/split-config.html
rename to pyscript.core/tests/manual/split-config.html
index 434426bc101..48bf8e71b80 100644
--- a/pyscript.core/test/split-config.html
+++ b/pyscript.core/tests/manual/split-config.html
@@ -5,13 +5,13 @@
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript Config</title>
         <script type="module">
-          import { config } from "../dist/core.js";
+          import { config } from "../../dist/core.js";
           console.log(config.mpy);
         </script>
-        <link rel="stylesheet" href="../dist/core.css">
+        <link rel="stylesheet" href="../../dist/core.css">
         <mpy-config>
           [[fetch]]
-          files = ["a.py"]
+          files = ["./a.py"]
         </mpy-config>
         <script type="mpy">
           import a
diff --git a/pyscript.core/test/target.html b/pyscript.core/tests/manual/target.html
similarity index 92%
rename from pyscript.core/test/target.html
rename to pyscript.core/tests/manual/target.html
index 90703d60ea3..525d0e79f3d 100644
--- a/pyscript.core/test/target.html
+++ b/pyscript.core/tests/manual/target.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>@pyscript/core</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <py-script id="first">
diff --git a/pyscript.core/test/test_display_HTML.html b/pyscript.core/tests/manual/test_display_HTML.html
similarity index 77%
rename from pyscript.core/test/test_display_HTML.html
rename to pyscript.core/tests/manual/test_display_HTML.html
index ffd44a7024c..5a0909f0a8f 100644
--- a/pyscript.core/test/test_display_HTML.html
+++ b/pyscript.core/tests/manual/test_display_HTML.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript Next: Display HTML</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <py-script>
diff --git a/pyscript.core/test/test_when.html b/pyscript.core/tests/manual/test_when.html
similarity index 81%
rename from pyscript.core/test/test_when.html
rename to pyscript.core/tests/manual/test_when.html
index 84d9b7ee63b..d322412e729 100644
--- a/pyscript.core/test/test_when.html
+++ b/pyscript.core/tests/manual/test_when.html
@@ -4,8 +4,8 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript Next: When Decorator</title>
-        <link rel="stylesheet" href="../dist/core.css">
-        <script type="module" src="../dist/core.js"></script>
+        <link rel="stylesheet" href="../../dist/core.css">
+        <script type="module" src="../../dist/core.js"></script>
     </head>
     <body>
         <p>Click for a hi!</p>
diff --git a/pyscript.core/test/worker.html b/pyscript.core/tests/manual/worker.html
similarity index 90%
rename from pyscript.core/test/worker.html
rename to pyscript.core/tests/manual/worker.html
index 0258328caec..5738444cc86 100644
--- a/pyscript.core/test/worker.html
+++ b/pyscript.core/tests/manual/worker.html
@@ -4,11 +4,11 @@
         <meta charset="UTF-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
         <title>PyScript Next</title>
-        <link rel="stylesheet" href="../dist/core.css">
+        <link rel="stylesheet" href="../../dist/core.css">
 
         <!-- the PyWorker approach -->
         <script type="module">
-          import { PyWorker, whenDefined } from '../dist/core.js';
+          import { PyWorker, whenDefined } from '../../dist/core.js';
           whenDefined('py').then(() => {
             PyWorker('./worker.py', {config: {fetch: [{files: ['./a.py']}]}});
           });
diff --git a/pyscript.core/test/worker.py b/pyscript.core/tests/manual/worker.py
similarity index 100%
rename from pyscript.core/test/worker.py
rename to pyscript.core/tests/manual/worker.py
diff --git a/pyscript.core/test/no_sab/index.html b/pyscript.core/tests/no_sab/index.html
similarity index 100%
rename from pyscript.core/test/no_sab/index.html
rename to pyscript.core/tests/no_sab/index.html
diff --git a/pyscript.core/test/no_sab/worker.py b/pyscript.core/tests/no_sab/worker.py
similarity index 100%
rename from pyscript.core/test/no_sab/worker.py
rename to pyscript.core/tests/no_sab/worker.py
diff --git a/pyscript.core/test/piratical.html b/pyscript.core/tests/piratical/index.html
similarity index 81%
rename from pyscript.core/test/piratical.html
rename to pyscript.core/tests/piratical/index.html
index 317f5d8fa5c..05c3204230d 100644
--- a/pyscript.core/test/piratical.html
+++ b/pyscript.core/tests/piratical/index.html
@@ -4,8 +4,8 @@
       <meta charset="utf-8" />
       <meta name="viewport" content="width=device-width,initial-scale=1" />
       <title>Arrr - Piratical PyScript</title>
-      <link rel="stylesheet" href="../dist/core.css" />
-      <script type="module" src="../dist/core.js"></script>
+      <link rel="stylesheet" href="../../dist/core.css" />
+      <script type="module" src="../../dist/core.js"></script>
   </head>
   <body>
     <h1>Arrr</h1>
diff --git a/pyscript.core/test/piratical.py b/pyscript.core/tests/piratical/piratical.py
similarity index 100%
rename from pyscript.core/test/piratical.py
rename to pyscript.core/tests/piratical/piratical.py
diff --git a/pyscript.core/test/piratical.toml b/pyscript.core/tests/piratical/piratical.toml
similarity index 100%
rename from pyscript.core/test/piratical.toml
rename to pyscript.core/tests/piratical/piratical.toml
diff --git a/pyscript.core/test/py-editor/config.toml b/pyscript.core/tests/py-editor/config.toml
similarity index 100%
rename from pyscript.core/test/py-editor/config.toml
rename to pyscript.core/tests/py-editor/config.toml
diff --git a/pyscript.core/test/py-editor/index.html b/pyscript.core/tests/py-editor/index.html
similarity index 100%
rename from pyscript.core/test/py-editor/index.html
rename to pyscript.core/tests/py-editor/index.html
diff --git a/pyscript.core/test/py-editor/issue-2056.html b/pyscript.core/tests/py-editor/issue-2056.html
similarity index 100%
rename from pyscript.core/test/py-editor/issue-2056.html
rename to pyscript.core/tests/py-editor/issue-2056.html
diff --git a/pyscript.core/test/py-editor/service-worker.html b/pyscript.core/tests/py-editor/service-worker.html
similarity index 100%
rename from pyscript.core/test/py-editor/service-worker.html
rename to pyscript.core/tests/py-editor/service-worker.html
diff --git a/pyscript.core/test/py-editor/sw.js b/pyscript.core/tests/py-editor/sw.js
similarity index 100%
rename from pyscript.core/test/py-editor/sw.js
rename to pyscript.core/tests/py-editor/sw.js
diff --git a/pyscript.core/test/py-editor/task1.py b/pyscript.core/tests/py-editor/task1.py
similarity index 100%
rename from pyscript.core/test/py-editor/task1.py
rename to pyscript.core/tests/py-editor/task1.py
diff --git a/pyscript.core/test/py-terminals/index.html b/pyscript.core/tests/py-terminals/index.html
similarity index 100%
rename from pyscript.core/test/py-terminals/index.html
rename to pyscript.core/tests/py-terminals/index.html
diff --git a/pyscript.core/test/py-terminals/no-repl.html b/pyscript.core/tests/py-terminals/no-repl.html
similarity index 100%
rename from pyscript.core/test/py-terminals/no-repl.html
rename to pyscript.core/tests/py-terminals/no-repl.html
diff --git a/pyscript.core/test/py-terminals/repl.html b/pyscript.core/tests/py-terminals/repl.html
similarity index 100%
rename from pyscript.core/test/py-terminals/repl.html
rename to pyscript.core/tests/py-terminals/repl.html
diff --git a/pyscript.core/test/pyscript_dom/index.html b/pyscript.core/tests/pyscript_dom/index.html
similarity index 97%
rename from pyscript.core/test/pyscript_dom/index.html
rename to pyscript.core/tests/pyscript_dom/index.html
index e92f99fa282..c8f71e0f969 100644
--- a/pyscript.core/test/pyscript_dom/index.html
+++ b/pyscript.core/tests/pyscript_dom/index.html
@@ -32,7 +32,7 @@
      </style>
   </head>
   <body>
-    <script type="py" src="/test/pyscript_dom/run_tests.py" config="/test/pyscript_dom/tests.toml"></script>
+    <script type="py" src="run_tests.py" config="tests.toml"></script>
 
     <h1>pyscript.dom Tests</h1>
     <p>You can pass test parameters to this test suite by passing them as query params on the url.
diff --git a/pyscript.core/test/pyscript_dom/run_tests.py b/pyscript.core/tests/pyscript_dom/run_tests.py
similarity index 100%
rename from pyscript.core/test/pyscript_dom/run_tests.py
rename to pyscript.core/tests/pyscript_dom/run_tests.py
diff --git a/pyscript.core/test/pyscript_dom/tests.toml b/pyscript.core/tests/pyscript_dom/tests.toml
similarity index 100%
rename from pyscript.core/test/pyscript_dom/tests.toml
rename to pyscript.core/tests/pyscript_dom/tests.toml
diff --git a/pyscript.core/test/pyscript_dom/tests/__init__.py b/pyscript.core/tests/pyscript_dom/tests/__init__.py
similarity index 100%
rename from pyscript.core/test/pyscript_dom/tests/__init__.py
rename to pyscript.core/tests/pyscript_dom/tests/__init__.py
diff --git a/pyscript.core/test/pyscript_dom/tests/conftest.py b/pyscript.core/tests/pyscript_dom/tests/conftest.py
similarity index 100%
rename from pyscript.core/test/pyscript_dom/tests/conftest.py
rename to pyscript.core/tests/pyscript_dom/tests/conftest.py
diff --git a/pyscript.core/test/pyscript_dom/tests/test_dom.py b/pyscript.core/tests/pyscript_dom/tests/test_dom.py
similarity index 100%
rename from pyscript.core/test/pyscript_dom/tests/test_dom.py
rename to pyscript.core/tests/pyscript_dom/tests/test_dom.py
diff --git a/pyscript.core/test/service-worker/index.html b/pyscript.core/tests/service-worker/index.html
similarity index 100%
rename from pyscript.core/test/service-worker/index.html
rename to pyscript.core/tests/service-worker/index.html
diff --git a/pyscript.core/test/service-worker/mini-coi.js b/pyscript.core/tests/service-worker/mini-coi.js
similarity index 100%
rename from pyscript.core/test/service-worker/mini-coi.js
rename to pyscript.core/tests/service-worker/mini-coi.js
diff --git a/pyscript.core/test/service-worker/sabayon.js b/pyscript.core/tests/service-worker/sabayon.js
similarity index 100%
rename from pyscript.core/test/service-worker/sabayon.js
rename to pyscript.core/tests/service-worker/sabayon.js
diff --git a/pyscript.core/test/ui/demo.py b/pyscript.core/tests/ui/demo.py
similarity index 100%
rename from pyscript.core/test/ui/demo.py
rename to pyscript.core/tests/ui/demo.py
diff --git a/pyscript.core/test/ui/examples.py b/pyscript.core/tests/ui/examples.py
similarity index 100%
rename from pyscript.core/test/ui/examples.py
rename to pyscript.core/tests/ui/examples.py
diff --git a/pyscript.core/test/ui/gallery.html b/pyscript.core/tests/ui/gallery.html
similarity index 100%
rename from pyscript.core/test/ui/gallery.html
rename to pyscript.core/tests/ui/gallery.html
diff --git a/pyscript.core/test/ui/gallery.py b/pyscript.core/tests/ui/gallery.py
similarity index 94%
rename from pyscript.core/test/ui/gallery.py
rename to pyscript.core/tests/ui/gallery.py
index 32196ffcd7f..d7172692df5 100644
--- a/pyscript.core/test/ui/gallery.py
+++ b/pyscript.core/tests/ui/gallery.py
@@ -167,7 +167,7 @@ def translate_markdown():
 )
 
 left_div.append(shoelace.Divider(style={"margin-top": "5px", "margin-bottom": "30px"}))
-left_div.append(el.a("Examples", href="/test/ui/", style={"text-align": "left"}))
+left_div.append(el.a("Examples", href="/tests/ui/", style={"text-align": "left"}))
 
 # ********** CREATE ALL THE LAYOUT **********
 grid = el.grid("minmax(100px, 200px) 20px auto", style={"min-height": "100%"})
@@ -176,5 +176,5 @@ def translate_markdown():
 grid.append(main_area)
 
 pydom.body.append(grid)
-pydom.body.append(el.a("Back to the main page", href="/test/ui/", target="_blank"))
-pydom.body.append(el.a("Hidden!!!", href="/test/ui/", target="_blank", hidden=True))
+pydom.body.append(el.a("Back to the main page", href="/tests/ui/", target="_blank"))
+pydom.body.append(el.a("Hidden!!!", href="/tests/ui/", target="_blank", hidden=True))
diff --git a/pyscript.core/test/ui/index.html b/pyscript.core/tests/ui/index.html
similarity index 100%
rename from pyscript.core/test/ui/index.html
rename to pyscript.core/tests/ui/index.html
diff --git a/pyscript.core/test/ui/pyscript.toml b/pyscript.core/tests/ui/pyscript.toml
similarity index 100%
rename from pyscript.core/test/ui/pyscript.toml
rename to pyscript.core/tests/ui/pyscript.toml
diff --git a/pyscript.core/test/ws.spec.js b/pyscript.core/tests/ws.spec.js
similarity index 100%
rename from pyscript.core/test/ws.spec.js
rename to pyscript.core/tests/ws.spec.js
diff --git a/pyscript.core/test/ws/index.html b/pyscript.core/tests/ws/index.html
similarity index 100%
rename from pyscript.core/test/ws/index.html
rename to pyscript.core/tests/ws/index.html
diff --git a/pyscript.core/test/ws/index.js b/pyscript.core/tests/ws/index.js
similarity index 100%
rename from pyscript.core/test/ws/index.js
rename to pyscript.core/tests/ws/index.js