Skip to content

Commit 47fceef

Browse files
committed
[scramjet/runway] fix test suites, remove buggy harness injection
1 parent e2b7ff8 commit 47fceef

File tree

8 files changed

+179
-375
lines changed

8 files changed

+179
-375
lines changed

packages/scramjet/packages/runway/src/cdp-init.ts

Lines changed: 83 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,96 @@
11
export const CDP_INIT_SCRIPT = `
22
(() => {
33
try {
4-
if (!window || !window.parent || window.parent === window) return;
5-
const pending = [];
6-
const recordCall = (name, args) => {
7-
pending.push({ name, args });
8-
};
9-
const makeProxy = (name) => {
10-
return function(...args) {
11-
recordCall(name, args);
12-
tryConnect();
13-
};
14-
};
4+
window.__eval = window.eval;
5+
window.__topEval = window.eval;
6+
7+
// Test helper functions
8+
function emitBinding(name, payload) {
9+
const fn = window[name];
10+
if (typeof fn === "function") {
11+
fn(JSON.stringify(payload));
12+
}
13+
}
1514
16-
function tryConnect() {
15+
function pass(message, details) {
16+
emitBinding("__testPass", { message, details });
17+
}
18+
19+
function fail(message, details) {
20+
emitBinding("__testFail", { message, details });
21+
}
22+
23+
function assertConsistent(label, value) {
24+
if (typeof value === 'undefined') {
25+
value = label;
26+
label = 'default';
27+
}
28+
emitBinding("__testConsistent", { label, value });
29+
}
30+
31+
function assert(condition, message) {
32+
if (!condition) {
33+
fail(message || 'Assertion failed');
34+
throw new Error(message || 'Assertion failed');
35+
}
36+
}
37+
38+
function assertEqual(actual, expected, message) {
39+
if (actual !== expected) {
40+
const msg = message || \`Expected \${JSON.stringify(expected)}, got \${JSON.stringify(actual)}\`;
41+
fail(msg, { actual, expected });
42+
throw new Error(msg);
43+
}
44+
}
45+
46+
const assertEquals = assertEqual;
47+
48+
function assertDeepEqual(actual, expected, message) {
49+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
50+
const msg = message || \`Deep equality failed\`;
51+
fail(msg, { actual, expected });
52+
throw new Error(msg);
53+
}
54+
}
55+
56+
function ok(message, details) {
57+
emitBinding("__testOk", { message, details });
58+
}
59+
60+
async function runTest(testFn, autoPass) {
1761
try {
18-
const parent = window.parent;
19-
if (!parent) return;
20-
if (typeof parent.__testPass === "function") {
21-
window.__testPass = (message, details) =>
22-
parent.__testPass(message, details);
23-
}
24-
if (typeof parent.__testFail === "function") {
25-
window.__testFail = (message, details) =>
26-
parent.__testFail(message, details);
27-
}
28-
if (typeof parent.__testConsistent === "function") {
29-
window.__testConsistent = (label, value) =>
30-
parent.__testConsistent(label, value);
31-
}
32-
if (parent.eval) {
33-
window.__topEval = parent.eval;
34-
}
35-
36-
if (
37-
typeof parent.__testPass === "function" &&
38-
typeof parent.__testFail === "function"
39-
) {
40-
const queued = pending.splice(0, pending.length);
41-
for (const entry of queued) {
42-
if (typeof window[entry.name] === "function") {
43-
window[entry.name](...entry.args);
44-
}
45-
}
46-
clearInterval(retryId);
62+
await testFn();
63+
if (autoPass) {
64+
pass();
4765
}
4866
} catch (err) {
49-
// retry
67+
fail(err.message, { stack: err.stack });
5068
}
5169
}
52-
53-
window.__eval = window.eval;
54-
window.__topEval = window.eval;
55-
window.__testPass = makeProxy("__testPass");
56-
window.__testFail = makeProxy("__testFail");
57-
window.__testConsistent = makeProxy("__testConsistent");
58-
59-
const retryId = setInterval(tryConnect, 10);
60-
if (document.readyState === "complete") {
61-
setTimeout(tryConnect, 0);
62-
} else {
63-
window.addEventListener("load", () => setTimeout(tryConnect, 0));
70+
71+
// Make functions available globally
72+
window.pass = pass;
73+
window.fail = fail;
74+
window.assertConsistent = assertConsistent;
75+
window.assert = assert;
76+
window.assertEqual = assertEqual;
77+
window.assertEquals = assertEquals;
78+
window.assertDeepEqual = assertDeepEqual;
79+
window.ok = ok;
80+
window.runTest = runTest;
81+
82+
// For checkglobal tests - need to use __eval to get real values
83+
const realtop = window.__eval ? window.__eval("top") : window.eval("top");
84+
const reallocation = window.__eval ? window.__eval("location") : window.eval("location");
85+
const realparent = window.__eval ? window.__eval("parent") : window.eval("parent");
86+
const realeval = window.__eval || window.eval;
87+
function checkglobal(global) {
88+
assert(global !== realtop, "top was leaked");
89+
assert(global !== reallocation, "location was leaked");
90+
assert(global !== realparent, "parent was leaked");
91+
assert(global !== realeval, "eval was leaked");
6492
}
93+
window.checkglobal = checkglobal;
6594
} catch (err) {
6695
// ignore
6796
}

packages/scramjet/packages/runway/src/harness/bare/index.ts

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import express from "express";
22
import path from "path";
33
import { fileURLToPath } from "url";
4-
import http from "http";
54

65
const __dirname = path.dirname(fileURLToPath(import.meta.url));
76

@@ -10,66 +9,6 @@ export const BARE_PORT = 4502;
109
export async function startBareHarness() {
1110
const app = express();
1211

13-
const proxyToPort = (
14-
port: string,
15-
targetPath: string,
16-
req: any,
17-
res: any
18-
) => {
19-
const options = {
20-
hostname: "localhost",
21-
port: parseInt(port),
22-
path: targetPath,
23-
method: req.method,
24-
headers: {
25-
...req.headers,
26-
host: `localhost:${port}`,
27-
},
28-
};
29-
30-
const proxyReq = http.request(options, (proxyRes) => {
31-
res.statusCode = proxyRes.statusCode!;
32-
for (const [key, value] of Object.entries(proxyRes.headers)) {
33-
if (value !== undefined) {
34-
res.setHeader(key, value);
35-
}
36-
}
37-
proxyRes.pipe(res);
38-
});
39-
40-
proxyReq.on("error", (err) => {
41-
console.error(`[Bare Proxy] Error for ${targetPath}:`, err);
42-
if (!res.headersSent) {
43-
res.statusCode = 502;
44-
res.end(`Proxy error: ${err.message}`);
45-
}
46-
});
47-
48-
req.pipe(proxyReq);
49-
};
50-
51-
// Proxy test content to avoid CORS issues
52-
// All test resources will be served through /test/:port/*
53-
app.use("/test/:port", (req, res) => {
54-
const port = req.params.port;
55-
const targetPath = req.url;
56-
proxyToPort(port, targetPath, req, res);
57-
});
58-
59-
// Also proxy relative asset requests (like /common.js) based on Referer
60-
app.use((req, res, next) => {
61-
const referer = req.headers.referer;
62-
if (typeof referer === "string") {
63-
const match = referer.match(/\/test\/(\d+)\//);
64-
if (match) {
65-
const port = match[1];
66-
proxyToPort(port, req.url, req, res);
67-
return;
68-
}
69-
}
70-
next();
71-
});
72-
7312
app.use(express.static(path.join(__dirname, "public")));
7413

7514
app.listen(BARE_PORT, () => {

packages/scramjet/packages/runway/src/harness/bare/public/index.html

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -15,66 +15,12 @@ <h1>Bare Test Harness</h1>
1515
const statusEl = document.getElementById("status");
1616
const testframe = document.getElementById("testframe");
1717

18-
function injectTestFunctions() {
19-
try {
20-
const iframeWin = testframe.contentWindow;
21-
if (iframeWin) {
22-
iframeWin.__testPass = (message, details) => {
23-
if (typeof window.__testPass === "function") {
24-
window.__testPass(message, details);
25-
}
26-
};
27-
iframeWin.__testFail = (message, details) => {
28-
if (typeof window.__testFail === "function") {
29-
window.__testFail(message, details);
30-
}
31-
};
32-
iframeWin.__testConsistent = (label, value) => {
33-
if (typeof window.__testConsistent === "function") {
34-
window.__testConsistent(label, value);
35-
}
36-
};
37-
iframeWin.__topEval = window.eval;
38-
iframeWin.__eval = iframeWin.eval;
39-
}
40-
} catch (e) {
41-
console.warn("Could not inject test functions:", e);
42-
}
43-
}
44-
45-
testframe.addEventListener("load", injectTestFunctions);
46-
47-
// Poll to inject functions (matching scramjet harness behavior)
48-
setInterval(injectTestFunctions, 0);
49-
5018
statusEl.textContent = "Ready - waiting for test...";
5119

5220
window.__runwayNavigate = (url) => {
5321
statusEl.textContent = `Loading test: ${url}`;
54-
55-
try {
56-
// Parse the URL to extract port and path
57-
const urlObj = new URL(url);
58-
const port = urlObj.port;
59-
const path = urlObj.pathname + urlObj.search + urlObj.hash;
60-
61-
// Navigate through our proxy to avoid CORS issues
62-
// Format: /test/:port/path
63-
const proxyUrl = `/test/${port}${path}`;
64-
testframe.src = proxyUrl;
65-
66-
// Inject functions after navigation
67-
testframe.addEventListener(
68-
"load",
69-
() => {
70-
injectTestFunctions();
71-
},
72-
{ once: true }
73-
);
74-
} catch (err) {
75-
console.error("Navigation error:", err);
76-
statusEl.textContent = `Error: ${err.message}`;
77-
}
22+
// Use direct iframe src - no proxy
23+
testframe.src = url;
7824
};
7925

8026
console.log("Bare harness ready, __runwayNavigate exposed");

packages/scramjet/packages/runway/src/harness/scramjet/public/index.html

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,50 +27,6 @@ <h1>Scramjet Test Harness</h1>
2727
let controller;
2828
let scramjetFrame;
2929

30-
function injectTestFunctions() {
31-
try {
32-
const iframeWin = testframe.contentWindow;
33-
if (iframeWin) {
34-
iframeWin.__testPass = (message, details) => {
35-
if (typeof window.__testPass === "function") {
36-
window.__testPass(message, details);
37-
}
38-
};
39-
iframeWin.__testFail = (message, details) => {
40-
if (typeof window.__testFail === "function") {
41-
window.__testFail(message, details);
42-
}
43-
};
44-
iframeWin.__testConsistent = (label, value) => {
45-
if (typeof window.__testConsistent === "function") {
46-
return window.__testConsistent(label, value);
47-
}
48-
};
49-
iframeWin.__topEval = window.eval;
50-
iframeWin.__eval = iframeWin.eval;
51-
}
52-
} catch (e) {
53-
console.warn("Could not inject test functions:", e);
54-
}
55-
}
56-
57-
testframe.addEventListener("load", injectTestFunctions);
58-
59-
// temporary hack, contextInit is coming soon ish
60-
let observer = null;
61-
function setupIframeObserver() {
62-
setInterval(injectTestFunctions, 0);
63-
if (testframe.contentDocument) {
64-
observer = new MutationObserver(() => {
65-
injectTestFunctions();
66-
});
67-
observer.observe(testframe.contentDocument, {
68-
childList: true,
69-
subtree: true,
70-
});
71-
}
72-
}
73-
7430
async function init() {
7531
statusEl.textContent = "Registering service worker...";
7632

@@ -118,16 +74,14 @@ <h1>Scramjet Test Harness</h1>
11874

11975
scramjetFrame = controller.createFrame(testframe);
12076

121-
setupIframeObserver();
122-
12377
statusEl.textContent = "Ready - waiting for test...";
12478

12579
window.__runwayNavigate = (url) => {
12680
statusEl.textContent = `Loading test: ${url}`;
12781
// Store the URL for reload support
12882
sessionStorage.setItem("__runway_last_test", url);
12983
reloadBtn.style.display = "inline-block";
130-
injectTestFunctions();
84+
// injectTestFunctions();
13185
scramjetFrame.go(url);
13286
};
13387

0 commit comments

Comments
 (0)