https://github.com/python/cpython/commit/ae4d27eba7c746463c62649c36d53979f3a00f94
commit: ae4d27eba7c746463c62649c36d53979f3a00f94
branch: main
author: Hood Chatham <roberthoodchat...@gmail.com>
committer: freakboy3742 <russ...@keith-magee.com>
date: 2025-07-24T14:44:02+08:00
summary:

gh-124621: Emscripten: Add smoke test for using pyrepl in Chrome (#137004)

Adds a mechanism to test browser-based initialisation of the Python interpreter,
via a Playwright headless browser instance.

files:
A Tools/wasm/emscripten/browser_test/.gitignore
A Tools/wasm/emscripten/browser_test/index.spec.ts
A Tools/wasm/emscripten/browser_test/package-lock.json
A Tools/wasm/emscripten/browser_test/package.json
A Tools/wasm/emscripten/browser_test/playwright.config.ts
A Tools/wasm/emscripten/browser_test/run_test.sh
M Tools/wasm/README.md
M Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html

diff --git a/Tools/wasm/README.md b/Tools/wasm/README.md
index 9288598a0abc29..efe9a3550c3e19 100644
--- a/Tools/wasm/README.md
+++ b/Tools/wasm/README.md
@@ -84,6 +84,17 @@ make a node application that "embeds" the interpreter 
instead of acting like the
 CLI you will need to write your own alternative to `node_entry.mjs`.
 
 
+### Running tests
+
+After building, you can run the full test suite with:
+```shell
+./cross-build/wasm32-emscripten/build/python/python.sh -m test -uall
+```
+You can run the browser smoke test with:
+```shell
+./Tools/wasm/emscripten/browser_test/run_test.sh
+```
+
 ### The Web Example
 
 When building for Emscripten, the web example will be built automatically. It
diff --git a/Tools/wasm/emscripten/browser_test/.gitignore 
b/Tools/wasm/emscripten/browser_test/.gitignore
new file mode 100644
index 00000000000000..3d54eab82a0d71
--- /dev/null
+++ b/Tools/wasm/emscripten/browser_test/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+test-results
+playwright-report
+test_log.txt
diff --git a/Tools/wasm/emscripten/browser_test/index.spec.ts 
b/Tools/wasm/emscripten/browser_test/index.spec.ts
new file mode 100644
index 00000000000000..2594b73022bcc1
--- /dev/null
+++ b/Tools/wasm/emscripten/browser_test/index.spec.ts
@@ -0,0 +1,15 @@
+import { test, expect } from '@playwright/test';
+
+test('has title', async ({ page }) => {
+  await page.goto('/');
+
+  await expect(page).toHaveTitle("Emscripten PyRepl Example");
+  const xterm = await page.locator('css=#terminal');
+  await expect(xterm).toHaveText(/Python.*on emscripten.*Type.*for more 
information/);
+  const xtermInput = await page.getByRole('textbox');
+  await xtermInput.pressSequentially(`def f():\nprint("hello", "emscripten 
repl!")\n\n`);
+  await xtermInput.pressSequentially(`f()\n`);
+  await expect(xterm).toHaveText(/hello emscripten repl!/);
+
+});
+
diff --git a/Tools/wasm/emscripten/browser_test/package-lock.json 
b/Tools/wasm/emscripten/browser_test/package-lock.json
new file mode 100644
index 00000000000000..044e3c19ce15f7
--- /dev/null
+++ b/Tools/wasm/emscripten/browser_test/package-lock.json
@@ -0,0 +1,672 @@
+{
+  "name": "browser_test",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "browser_test",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "@playwright/test": "^1.54.1",
+        "@types/node": "^24.1.0",
+        "http-server": "^14.1.1",
+        "playwright": "^1.54.1"
+      }
+    },
+    "node_modules/@playwright/test": {
+      "version": "1.54.1",
+      "resolved": 
"https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz";,
+      "integrity": 
"sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "playwright": "1.54.1"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "24.1.0",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz";,
+      "integrity": 
"sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.8.0"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": 
"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz";,
+      "integrity": 
"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1";
+      }
+    },
+    "node_modules/async": {
+      "version": "3.2.6",
+      "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz";,
+      "integrity": 
"sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+      "license": "MIT"
+    },
+    "node_modules/basic-auth": {
+      "version": "2.0.1",
+      "resolved": 
"https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz";,
+      "integrity": 
"sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "5.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": 
"https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz";,
+      "integrity": 
"sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": 
"https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz";,
+      "integrity": 
"sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz";,
+      "integrity": 
"sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1";
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": 
"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz";,
+      "integrity": 
"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": 
"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz";,
+      "integrity": 
"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "license": "MIT"
+    },
+    "node_modules/corser": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz";,
+      "integrity": 
"sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz";,
+      "integrity": 
"sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": 
"https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz";,
+      "integrity": 
"sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": 
"https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz";,
+      "integrity": 
"sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz";,
+      "integrity": 
"sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": 
"https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz";,
+      "integrity": 
"sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": 
"https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz";,
+      "integrity": 
"sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": 
"https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz";,
+      "integrity": 
"sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh";
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz";,
+      "integrity": 
"sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": 
"https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz";,
+      "integrity": 
"sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": 
"https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz";,
+      "integrity": 
"sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz";,
+      "integrity": 
"sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz";,
+      "integrity": 
"sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz";,
+      "integrity": 
"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": 
"https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz";,
+      "integrity": 
"sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz";,
+      "integrity": 
"sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz";,
+      "integrity": 
"sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "license": "MIT",
+      "bin": {
+        "he": "bin/he"
+      }
+    },
+    "node_modules/html-encoding-sniffer": {
+      "version": "3.0.0",
+      "resolved": 
"https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz";,
+      "integrity": 
"sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-encoding": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/http-proxy": {
+      "version": "1.18.1",
+      "resolved": 
"https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz";,
+      "integrity": 
"sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+      "license": "MIT",
+      "dependencies": {
+        "eventemitter3": "^4.0.0",
+        "follow-redirects": "^1.0.0",
+        "requires-port": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/http-server": {
+      "version": "14.1.1",
+      "resolved": 
"https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz";,
+      "integrity": 
"sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==",
+      "license": "MIT",
+      "dependencies": {
+        "basic-auth": "^2.0.1",
+        "chalk": "^4.1.2",
+        "corser": "^2.0.1",
+        "he": "^1.2.0",
+        "html-encoding-sniffer": "^3.0.0",
+        "http-proxy": "^1.18.1",
+        "mime": "^1.6.0",
+        "minimist": "^1.2.6",
+        "opener": "^1.5.1",
+        "portfinder": "^1.0.28",
+        "secure-compare": "3.0.1",
+        "union": "~0.5.0",
+        "url-join": "^4.0.1"
+      },
+      "bin": {
+        "http-server": "bin/http-server"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": 
"https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz";,
+      "integrity": 
"sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": 
"https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz";,
+      "integrity": 
"sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz";,
+      "integrity": 
"sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz";,
+      "integrity": 
"sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz";,
+      "integrity": 
"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": 
"https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz";,
+      "integrity": 
"sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/opener": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz";,
+      "integrity": 
"sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==",
+      "license": "(WTFPL OR MIT)",
+      "bin": {
+        "opener": "bin/opener-bin.js"
+      }
+    },
+    "node_modules/playwright": {
+      "version": "1.54.1",
+      "resolved": 
"https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz";,
+      "integrity": 
"sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "playwright-core": "1.54.1"
+      },
+      "bin": {
+        "playwright": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "fsevents": "2.3.2"
+      }
+    },
+    "node_modules/playwright-core": {
+      "version": "1.54.1",
+      "resolved": 
"https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz";,
+      "integrity": 
"sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==",
+      "license": "Apache-2.0",
+      "bin": {
+        "playwright-core": "cli.js"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/portfinder": {
+      "version": "1.0.37",
+      "resolved": 
"https://registry.npmjs.org/portfinder/-/portfinder-1.0.37.tgz";,
+      "integrity": 
"sha512-yuGIEjDAYnnOex9ddMnKZEMFE0CcGo6zbfzDklkmT1m5z734ss6JMzN9rNB3+RR7iS+F10D4/BVIaXOyh8PQKw==",
+      "license": "MIT",
+      "dependencies": {
+        "async": "^3.2.6",
+        "debug": "^4.3.6"
+      },
+      "engines": {
+        "node": ">= 10.12"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz";,
+      "integrity": 
"sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/requires-port": {
+      "version": "1.0.0",
+      "resolved": 
"https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz";,
+      "integrity": 
"sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+      "license": "MIT"
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": 
"https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz";,
+      "integrity": 
"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": 
"https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz";,
+      "integrity": 
"sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/secure-compare": {
+      "version": "3.0.1",
+      "resolved": 
"https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz";,
+      "integrity": 
"sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==",
+      "license": "MIT"
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": 
"https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz";,
+      "integrity": 
"sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": 
"https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz";,
+      "integrity": 
"sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": 
"https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz";,
+      "integrity": 
"sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": 
"https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz";,
+      "integrity": 
"sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb";
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": 
"https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz";,
+      "integrity": 
"sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "7.8.0",
+      "resolved": 
"https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz";,
+      "integrity": 
"sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
+      "license": "MIT"
+    },
+    "node_modules/union": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz";,
+      "integrity": 
"sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
+      "dependencies": {
+        "qs": "^6.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/url-join": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz";,
+      "integrity": 
"sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
+      "license": "MIT"
+    },
+    "node_modules/whatwg-encoding": {
+      "version": "2.0.0",
+      "resolved": 
"https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz";,
+      "integrity": 
"sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
+      "license": "MIT",
+      "dependencies": {
+        "iconv-lite": "0.6.3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    }
+  }
+}
diff --git a/Tools/wasm/emscripten/browser_test/package.json 
b/Tools/wasm/emscripten/browser_test/package.json
new file mode 100644
index 00000000000000..3320d4cccd0594
--- /dev/null
+++ b/Tools/wasm/emscripten/browser_test/package.json
@@ -0,0 +1,18 @@
+{
+  "name": "browser_test",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "description": "",
+  "dependencies": {
+    "@playwright/test": "^1.54.1",
+    "@types/node": "^24.1.0",
+    "http-server": "^14.1.1",
+    "playwright": "^1.54.1"
+  }
+}
diff --git a/Tools/wasm/emscripten/browser_test/playwright.config.ts 
b/Tools/wasm/emscripten/browser_test/playwright.config.ts
new file mode 100644
index 00000000000000..81d53ce11cb050
--- /dev/null
+++ b/Tools/wasm/emscripten/browser_test/playwright.config.ts
@@ -0,0 +1,22 @@
+import { defineConfig, devices } from '@playwright/test';
+
+export default defineConfig({
+  testDir: '.',
+  forbidOnly: true,
+  retries: 2,
+  reporter: process.env.CI ? 'dot' : 'html',
+  use: {
+    baseURL: 'http://localhost:8787',
+    trace: 'on-first-retry',
+  },
+  projects: [
+    {
+      name: 'chromium',
+      use: { ...devices['Desktop Chrome'] },
+    },
+  ],
+  webServer: {
+    command: 'npx http-server 
../../../../cross-build/wasm32-emscripten/build/python/web_example_pyrepl_jspi/ 
-p 8787',
+    url: 'http://localhost:8787',
+  },
+});
diff --git a/Tools/wasm/emscripten/browser_test/run_test.sh 
b/Tools/wasm/emscripten/browser_test/run_test.sh
new file mode 100755
index 00000000000000..9166e0d740585e
--- /dev/null
+++ b/Tools/wasm/emscripten/browser_test/run_test.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+set -euo pipefail
+cd "$(dirname "$0")"
+rm -f test_log.txt
+echo "Installing node packages" | tee test_log.txt
+npm ci >> test_log.txt 2>&1
+echo "Installing playwright browsers" | tee test_log.txt
+npx playwright install 2>> test_log.txt
+echo "Running tests" | tee test_log.txt
+CI=1 npx playwright test | tee test_log.txt
diff --git a/Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html 
b/Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html
index 1f72bd24e79a04..cc7413b5caa982 100644
--- a/Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html
+++ b/Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html
@@ -1,6 +1,7 @@
 <!doctype html>
 <html>
     <head>
+        <title>Emscripten PyRepl Example</title>
         <link
             rel="stylesheet"
             href="https://unpkg.com/xterm@4.18.0/css/xterm.css";

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to