124 lines
5.0 KiB
TypeScript
124 lines
5.0 KiB
TypeScript
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");
|
|
});
|