(cap)
| 26 | }; |
| 27 | |
| 28 | async function runCapability(cap) { |
| 29 | const dir = `${ROOT}${cap}`; |
| 30 | // Import the capability's `.py` entry when it has one, else the JS host module. |
| 31 | const hasPy = existsSync(`${dir}/src/entry.py`); |
| 32 | |
| 33 | const cases = JSON.parse(readFileSync(`${dir}/${cap}.json`, "utf-8")); |
| 34 | // The tag's packages.json, synthesized: python -> entry.py as a code module; else the JS host module. |
| 35 | const manifest = JSON.stringify( |
| 36 | hasPy |
| 37 | ? { imports: { [cap]: `/${cap}/src/entry.py` } } |
| 38 | : { host: { [cap]: `/${cap}/src/index.js` } }, |
| 39 | ); |
| 40 | |
| 41 | const browser = await chromium.launch(); |
| 42 | const page = await browser.newPage(); |
| 43 | const errors = []; |
| 44 | page.on("console", (m) => { if (m.type() === "error") errors.push(m.text()); }); |
| 45 | page.on("pageerror", (e) => errors.push(e.message)); |
| 46 | |
| 47 | /* Serve repo files from disk; synthesize the manifest. External CDNs (cdn.edgepython.com) pass through. */ |
| 48 | await page.route("**/*", (route) => { |
| 49 | const url = new URL(route.request().url()); |
| 50 | if (url.host !== "localhost") return route.continue(); |
| 51 | if (url.pathname === MANIFEST) return route.fulfill({ contentType: "application/json", body: manifest }); |
| 52 | const path = ROOT + url.pathname.slice(1); |
| 53 | try { |
| 54 | const ext = path.slice(path.lastIndexOf(".")); |
| 55 | return route.fulfill({ body: readFileSync(path), contentType: TYPES[ext] ?? "application/octet-stream" }); |
| 56 | } catch { |
| 57 | return route.fulfill({ status: 404 }); |
| 58 | } |
| 59 | }); |
| 60 | |
| 61 | /* Per-case mocks; uninstalled after each case so they don't leak into later ones. */ |
| 62 | const httpMocks = []; |
| 63 | const installMocks = async (c) => { |
| 64 | for (const m of c.http_mocks ?? []) { |
| 65 | const handler = (route) => route.fulfill({ |
| 66 | status: m.status ?? 200, |
| 67 | contentType: m.contentType ?? "application/json", |
| 68 | body: m.body ?? "", |
| 69 | }); |
| 70 | await page.route(m.url, handler); |
| 71 | httpMocks.push({ url: m.url, handler }); |
| 72 | } |
| 73 | for (const m of c.ws_mocks ?? []) { |
| 74 | await page.routeWebSocket(m.url, (ws) => { |
| 75 | if (m.echo) ws.onMessage((message) => ws.send(message)); |
| 76 | }); |
| 77 | } |
| 78 | }; |
| 79 | const uninstallMocks = async () => { |
| 80 | for (const { url, handler } of httpMocks.splice(0)) await page.unroute(url, handler); |
| 81 | }; |
| 82 | |
| 83 | const failures = []; |
| 84 | try { |
| 85 | await page.goto("http://localhost/tests/index.html"); |
no test coverage detected