feat: add Python and Java regex engines

This commit is contained in:
2026-07-27 02:02:21 +02:00
parent 4951c966d4
commit 7079cde15f
95 changed files with 8866 additions and 251 deletions

View File

@@ -0,0 +1,123 @@
import { expect, test, type Page } from "@playwright/test";
async function setEditor(page: Page, testId: string, value: string) {
await page.getByTestId(testId).locator(".cm-content").fill(value);
}
async function waitForReady(page: Page, timeout = 15_000) {
await expect(page.locator(".run-status")).toHaveClass(/status-ready/u, {
timeout,
});
}
test("CPython re executes and replaces with code-point-native offsets under the production CSP", async ({
page,
}) => {
const pageErrors: string[] = [];
const consoleErrors: string[] = [];
page.on("pageerror", (error) => pageErrors.push(error.message));
page.on("console", (message) => {
if (message.type() === "error") consoleErrors.push(message.text());
});
const response = await page.goto("/deep/nested/regex/");
expect(response?.headers()["content-security-policy"]).toContain(
"script-src 'self' 'wasm-unsafe-eval'",
);
expect(response?.headers()["content-security-policy"]).not.toContain(
"'unsafe-eval'",
);
await waitForReady(page);
await page.getByLabel("Live").uncheck();
await page.getByRole("combobox", { name: "Flavour" }).selectOption("python");
await setEditor(page, "editor-regular-expression-pattern", "(?P<word>\\w+)");
await setEditor(page, "editor-test-text", "😀 café");
await page.getByRole("button", { name: "Run", exact: true }).click();
await waitForReady(page, 45_000);
await expect(page.locator(".run-status")).toContainText(
"native offsets code-point",
);
const match = page.locator(".extraction-row").first();
await expect(match).toContainText("UTF-16 range: 3…7");
await expect(match).toContainText("native range: 2…6 code-point");
await page.getByRole("button", { name: "Replace", exact: true }).click();
await setEditor(page, "editor-replacement-template", "[\\g<word>]");
await page.getByRole("button", { name: "Run", exact: true }).click();
await waitForReady(page, 45_000);
await expect(
page.getByTestId("editor-replacement-output").locator(".cm-content"),
).toHaveText("😀 [café]");
await page.getByRole("button", { name: "Quick reference" }).click();
await expect(
page.getByRole("dialog", { name: "Quick reference" }),
).toContainText("Python 3.14 re documentation");
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "Capabilities" }).click();
const capabilities = page.getByRole("dialog", { name: "Capabilities" });
await expect(capabilities).toContainText("CPython re via Pyodide");
await expect(capabilities).toContainText("CPython 3.14.2 re");
expect(pageErrors).toEqual([]);
expect(consoleErrors).toEqual([]);
});
test("TeaVM java.util.regex executes Java-only syntax and numeric replacement with UTF-16 offsets", async ({
page,
}) => {
const pageErrors: string[] = [];
page.on("pageerror", (error) => pageErrors.push(error.message));
await page.goto("/deep/nested/regex/");
await waitForReady(page);
await page.getByLabel("Live").uncheck();
await page.getByRole("combobox", { name: "Flavour" }).selectOption("java");
await setEditor(
page,
"editor-regular-expression-pattern",
"(?<word>[a-z]++)",
);
await setEditor(page, "editor-test-text", "😀 abc");
await page.getByRole("button", { name: "Run", exact: true }).click();
await waitForReady(page);
await expect(page.locator(".run-status")).toContainText(
"native offsets utf16",
);
const match = page.locator(".extraction-row").first();
await expect(match).toContainText("UTF-16 range: 3…6");
await expect(match).toContainText("native range: 3…6 utf16");
await page.getByRole("button", { name: "Replace", exact: true }).click();
await setEditor(page, "editor-replacement-template", "<$1>");
await page.getByRole("button", { name: "Run", exact: true }).click();
await waitForReady(page);
await expect(
page.getByTestId("editor-replacement-output").locator(".cm-content"),
).toHaveText("😀 <abc>");
await page.getByRole("button", { name: "Capabilities" }).click();
const capabilities = page.getByRole("dialog", { name: "Capabilities" });
await expect(capabilities).toContainText("TeaVM java.util.regex");
await expect(capabilities).toContainText("not OpenJDK");
expect(pageErrors).toEqual([]);
});
test("serves the self-hosted Python and Java runtime assets with executable MIME types", async ({
request,
}) => {
const pythonModule = await request.get("./engines/python/pyodide.asm.mjs");
expect(pythonModule.ok()).toBe(true);
expect(pythonModule.headers()["content-type"]).toContain("text/javascript");
const pythonWasm = await request.get("./engines/python/pyodide.asm.wasm");
expect(pythonWasm.ok()).toBe(true);
expect(pythonWasm.headers()["content-type"]).toContain("application/wasm");
expect((await pythonWasm.body()).subarray(0, 4)).toEqual(
Buffer.from([0x00, 0x61, 0x73, 0x6d]),
);
const javaModule = await request.get("./engines/java/java-regex.mjs");
expect(javaModule.ok()).toBe(true);
expect(javaModule.headers()["content-type"]).toContain("text/javascript");
});