feat: add Python and Java regex engines
This commit is contained in:
123
tests/browser/flavours.spec.ts
Normal file
123
tests/browser/flavours.spec.ts
Normal 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");
|
||||
});
|
||||
Reference in New Issue
Block a user