Skip to content

Commit

Permalink
Use real http server for browser testing
Browse files Browse the repository at this point in the history
It seems like playwright's request interception does not work with large
fiels like .wasm
  • Loading branch information
kateinoigakukun committed Nov 21, 2024
1 parent b91944c commit ed5680a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 23 deletions.
6 changes: 3 additions & 3 deletions Sources/CartonHelpers/StaticArchive.swift

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions Sources/carton-frontend-slim/BundleLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ struct BundleLayout {
<body>
<script type="module">
import { testBrowser } from "./index.js";
testBrowser([], true);
testBrowser([], {}, true);
</script>
</body>
Expand Down Expand Up @@ -209,8 +209,8 @@ struct BundleLayout {
return internalInstantiate(options, imports);
}
export async function testBrowser(args, inPage = false) {
await internalTestBrowser(instantiate, wasmFileName, args, import.meta.url, inPage);
export async function testBrowser(args, options, inPage = false) {
await internalTestBrowser(instantiate, wasmFileName, args, import.meta.url, options, inPage);
}
export async function testNode(args) {
Expand Down
3 changes: 2 additions & 1 deletion Sources/carton-release/HashArchive.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ struct HashArchive: AsyncParsableCommand {
let arguments = [
"esbuild", "--bundle", "entrypoint/\(tsFilename)", "--outfile=static/\(filename)",
"--external:node:url", "--external:node:path",
"--external:node:module", "--external:node:fs/promises",
"--external:node:module", "--external:node:http",
"--external:node:fs/promises", "--external:node:fs",
"--external:playwright",
"--format=esm",
"--external:./JavaScriptKit_JavaScriptKit.resources/Runtime/index.mjs",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export class SwiftRuntime {
constructor({ sharedMemory: booleam });
setInstance(instance: WebAssembly.Instance): void;
main?(): void;
readonly wasmImports: ImportedFunctions;
Expand Down
71 changes: 55 additions & 16 deletions entrypoint/intrinsics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { WASI, File, OpenFile, ConsoleStdout, PreopenDirectory, WASIProcExit, In
import type { SwiftRuntime, SwiftRuntimeConstructor } from "./JavaScriptKit_JavaScriptKit.resources/Runtime/index";
import { polyfill as polyfillWebAssemblyTypeReflection } from "wasm-imports-parser/polyfill";
import type { ImportEntry } from "wasm-imports-parser";
import { AddressInfo } from "node:net";

// Apply polyfill for WebAssembly Type Reflection JS API to inspect imported memory info.
// https://github.com/WebAssembly/js-types/blob/main/proposals/js-types/Overview.md
Expand Down Expand Up @@ -65,7 +66,14 @@ export async function instantiate(rawOptions: InstantiationOptions, extraWasmImp

let swift: SwiftRuntime | undefined = options.swift;
if (!swift && options.SwiftRuntime) {
swift = new options.SwiftRuntime();
let sharedMemory = false;
for (const importEntry of WebAssembly.Module.imports(options.module)) {
if (importEntry.module === "env" && importEntry.name === "memory" && importEntry.kind === "memory") {
sharedMemory = true;
break;
}
}
swift = new options.SwiftRuntime({ sharedMemory });
}

let stdoutLine: LineDecoder | undefined = undefined;
Expand Down Expand Up @@ -242,10 +250,54 @@ async function extractAndSaveFile(rootFs: Map<string, Inode>, path: string): Pro
return false;
}

export async function testBrowser(instantiate: Instantiate, wasmFileName: string, args: string[], indexJsUrl: string, inPage: boolean) {
export async function testBrowser(
instantiate: Instantiate,
wasmFileName: string,
args: string[],
indexJsUrl: string,
options: { contentTypes?: (fileName: string) => string } = {},
inPage: boolean = false
) {
if (inPage) {
return await testBrowserInPage(instantiate, wasmFileName, args);
}

const { fileURLToPath } = await import("node:url");
const path = await import("node:path");
const fs = await import("node:fs/promises");
const { existsSync } = await import("node:fs");
const indexJsPath = fileURLToPath(indexJsUrl);
const webRoot = path.dirname(indexJsPath);

const http = await import("node:http");
const defaultContentTypes: Record<string, string> = {
".html": "text/html",
".js": "text/javascript",
".mjs": "text/javascript",
".wasm": "application/wasm",
};
const server = http.createServer(async (req, res) => {
const url = new URL(req.url!, `http://${req.headers.host}`);
const pathname = url.pathname;
const filePath = path.join(webRoot, pathname);
if (existsSync(filePath) && (await fs.stat(filePath)).isFile()) {
const data = await fs.readFile(filePath);
const ext = pathname.slice(pathname.lastIndexOf("."));
const contentType = options.contentTypes?.(pathname) || defaultContentTypes[ext] || "text/plain";
res.writeHead(200, { "Content-Type": contentType });
res.end(data);
} else if (pathname === "/process-info.json") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ env: process.env }));
} else {
res.writeHead(404);
res.end();
}
});

await new Promise<void>((resolve) => server.listen({ host: "localhost", port: 0 }, () => resolve()));
const address = server.address() as AddressInfo;

const playwright = await (async () => {
try {
// @ts-ignore
Expand All @@ -263,29 +315,16 @@ Please run the following command to install it:
const browser = await playwright.chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
const { fileURLToPath } = await import("node:url");
const path = await import("node:path");
const indexJsPath = fileURLToPath(indexJsUrl);
const webRoot = path.dirname(indexJsPath);

// Forward console messages in the page to the Node.js console
page.on("console", (message: any) => {
console.log(message.text());
});

await page.route("http://example.com/**/*", async (route: any) => {
const url = route.request().url();
const urlPath = new URL(url).pathname;
if (urlPath === "/process-info.json") {
route.fulfill({ body: JSON.stringify({ env: process.env }) });
return;
}
route.fulfill({ path: path.join(webRoot, urlPath.slice(1)) });
});
const onExit = new Promise<number>((resolve) => {
page.exposeFunction("exitTest", resolve);
});
await page.goto("http://example.com/test.browser.html");
await page.goto(`http://localhost:${address.port}/test.browser.html`);
const exitCode = await onExit;
await browser.close();
process.exit(exitCode);
Expand Down

0 comments on commit ed5680a

Please sign in to comment.