88 lines
4.8 KiB
TypeScript
88 lines
4.8 KiB
TypeScript
import { expect, test, type Page } from "@playwright/test";
|
|
import { mockAgentApi } from "./support/mock-agent-api";
|
|
import { mockWorkbenchContext } from "./support/mock-workbench-context";
|
|
import { expectThemeColor } from "./support/theme-colors";
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.emulateMedia({ reducedMotion: "reduce" });
|
|
await mockWorkbenchContext(page);
|
|
await mockAgentApi(page);
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
});
|
|
|
|
for (const width of [320, 375, 1440]) {
|
|
test(`layer panel basemap selection has one border and no persistent outer ring at ${width}px`, async ({ page }) => {
|
|
await page.setViewportSize({ width, height: 1000 });
|
|
await page.getByRole("button", { name: /图层:管理地图图层/ }).click();
|
|
const panel = page.getByRole("region", { name: "图层", exact: true });
|
|
const card = panel.getByRole("button", { name: "无底图", exact: true });
|
|
await expect(card).toHaveAttribute("aria-pressed", "true");
|
|
await expectThemeColor(card, "border-color", "--action-blue");
|
|
await expect(card).toHaveCSS("border-width", "1px");
|
|
await expect(card).toHaveCSS("box-shadow", "none");
|
|
await card.click();
|
|
await expect(card).toHaveCSS("outline-style", "none");
|
|
const restingBox = await card.boundingBox();
|
|
await page.keyboard.press("Tab");
|
|
await card.focus();
|
|
await expect(card).toHaveCSS("outline-width", "1px");
|
|
await expect(card).toHaveCSS("outline-offset", "-1px");
|
|
await expectThemeColor(card, "outline-color", "--action-focus-ring");
|
|
expect(await card.boundingBox()).toEqual(restingBox);
|
|
await page.keyboard.press("Enter");
|
|
await expect(card).toHaveAttribute("aria-pressed", "true");
|
|
await panel.screenshot({ path: `test-results/form-focus/layer-panel-${width}.png` });
|
|
});
|
|
}
|
|
|
|
test("standalone basemap controls share the same pointer and keyboard boundary", async ({ page }) => {
|
|
await renderControls(page);
|
|
for (const label of ["面板底图", "浮动底图"]) {
|
|
const control = page.getByRole("region", { name: label, exact: true });
|
|
if (label === "浮动底图") await control.getByRole("button", { name: "浮动底图:当前 浅色" }).hover();
|
|
const selected = control.getByRole("button", { name: "浅色", exact: true });
|
|
await expect(selected).toHaveCSS("border-width", "1px");
|
|
await expectThemeColor(selected, "border-color", "--action-blue");
|
|
const restingShadow = await selected.evaluate((element) => getComputedStyle(element).boxShadow);
|
|
expect(restingShadow).not.toMatch(/0px 0px 0px [12]px/);
|
|
await selected.click();
|
|
await expect(selected).toHaveCSS("outline-style", "none");
|
|
await page.keyboard.press("Tab");
|
|
await selected.focus();
|
|
await expect(selected).toHaveCSS("outline-width", "1px");
|
|
await expect(selected).toHaveCSS("outline-offset", "-1px");
|
|
await expect(selected).toHaveCSS("box-shadow", restingShadow);
|
|
const next = control.getByRole("button", { name: "影像", exact: true });
|
|
await next.focus();
|
|
await page.keyboard.press("Enter");
|
|
await expect(next).toHaveAttribute("aria-pressed", "true");
|
|
await expect(selected).toHaveAttribute("aria-pressed", "false");
|
|
await control.screenshot({ path: `test-results/form-focus/${label}.png` });
|
|
}
|
|
});
|
|
|
|
async function renderControls(page: Page) {
|
|
await page.evaluate(async () => {
|
|
const entry = await fetch("/src/main.tsx").then((response) => response.text());
|
|
const reactPath = entry.match(/["']([^"']*\/react\.js(?:\?[^"']*)?)["']/)?.[1];
|
|
const domPath = entry.match(/["']([^"']*\/react-dom_client\.js(?:\?[^"']*)?)["']/)?.[1];
|
|
if (!reactPath || !domPath) throw new Error("App React dependency URLs not found");
|
|
const paths = [reactPath, domPath, "/src/features/map/core/components/base-layers-control.tsx"];
|
|
const [react, dom, controls] = await Promise.all(paths.map((path) => import(path)));
|
|
const h = react.default.createElement;
|
|
function Examples() {
|
|
const [panelId, setPanelId] = react.default.useState("light");
|
|
const [floatingId, setFloatingId] = react.default.useState("light");
|
|
const options = [{ id: "light", label: "浅色" }, { id: "satellite", label: "影像" }];
|
|
return h(react.default.Fragment, null,
|
|
h(controls.BaseLayersControl, { title: "面板底图", variant: "panel", options, activeLayerId: panelId, onSelectLayer: setPanelId }),
|
|
h(controls.BaseLayersControl, { title: "浮动底图", options, activeLayerId: floatingId, onSelectLayer: setFloatingId })
|
|
);
|
|
}
|
|
const container = document.createElement("div");
|
|
container.style.cssText = "position:fixed;z-index:40;top:80px;right:40px;padding:24px;background:var(--surface-reading);display:grid;gap:32px";
|
|
document.body.append(container);
|
|
dom.default.createRoot(container).render(h(Examples));
|
|
});
|
|
}
|