Files
openidea/e2e/settingsFormLayout.spec.ts
T
l0ng-ai 7f88d3d723 fix(settings): consistent form columns, font page metrics and sidebar width (#2388)
Settings forms now share one label/field column layout, the Editor › Font page uses consistent field sizes, and the sidebar width is resizable from the keyboard and remembered. The Settings window's minimum size grows to 900×700 so pages no longer squeeze below their layout; fitting a window over its parent now keeps the comparison and deployment windows' own minimum sizes instead of the Settings one.
2026-11-10 03:46:22 +08:00

90 lines
4.4 KiB
TypeScript

import type { Page } from "@playwright/test";
import { expect, test } from "./fixtures";
async function selectPage(page: Page, id: string) {
await page.evaluate(async (id) => {
const path = "/src/features/settings/model/staging.ts";
(await import(/* @vite-ignore */ path)).selectPage(id);
}, id);
}
test("settings forms share control columns and keep font metrics on one row", async ({ workbench: page }) => {
await page.keyboard.press("Meta+,");
const dialog = page.getByRole("dialog", { name: "Settings", exact: true });
await expect(dialog).toBeVisible();
await selectPage(page, "appearance");
const controls = ["Theme", "Editor color scheme"].map((name) => dialog.getByRole("button", { name, exact: true }));
for (const control of controls) await expect(control).toBeVisible();
const boxes = await Promise.all(controls.map((control) => control.boundingBox()));
for (const box of boxes) expect(box!.x).toBeCloseTo(boxes[0]!.x, 0);
await selectPage(page, "editor.font");
const font = dialog.getByRole("combobox", { name: "Font", exact: true });
const size = dialog.getByRole("textbox", { name: "Size", exact: true });
const line = dialog.getByRole("textbox", { name: "Line height", exact: true });
await expect(line).toBeVisible();
const [fontBox, sizeBox, lineBox] = await Promise.all([font.boundingBox(), size.boundingBox(), line.boundingBox()]);
expect(fontBox!.x).toBeCloseTo(sizeBox!.x, 0);
expect(lineBox!.y).toBeCloseTo(sizeBox!.y, 0);
expect(lineBox!.x).toBeGreaterThan(sizeBox!.x + sizeBox!.width);
const weights = await Promise.all(
["Main font weight", "Bold font weight"].map((name) =>
dialog.getByRole("button", { name, exact: true }).boundingBox(),
),
);
const fallback = await dialog.getByRole("combobox", { name: "Fallback font", exact: true }).boundingBox();
for (const box of weights) expect(box!.x).toBeCloseTo(fallback!.x, 0);
for (const name of ["Main font weight", "Bold font weight", "Fallback font"]) {
const singleLine = await dialog.getByText(name, { exact: true }).evaluate((el) => {
const range = document.createRange();
range.selectNodeContents(el);
return range.getBoundingClientRect().height <= Number.parseFloat(getComputedStyle(el).lineHeight) + 1;
});
expect(singleLine, name).toBe(true);
}
});
for (const locale of ["en", "zh"]) {
test(`${locale} forms fit narrow settings panes without horizontal scrolling`, async ({ workbench: page }) => {
await page.keyboard.press("Meta+,");
await expect(page.getByRole("dialog", { name: "Settings", exact: true })).toBeVisible();
await page.evaluate(async (locale) => {
const path = "/src/platform/i18n.ts";
await (await import(/* @vite-ignore */ path)).setLocale(locale);
}, locale);
for (const id of ["appearance", "editor.font", "general", "general.httpProxy"]) {
await selectPage(page, id);
const body = page.locator("[class*='pageBody']");
for (const width of [1000, 784, 640, 520]) {
await page.setViewportSize({ width, height: 760 });
await expect
.poll(() => body.evaluate((el) => el.scrollWidth - el.clientWidth), { message: `${id} at ${width}px` })
.toBeLessThanOrEqual(1);
}
}
});
}
test("settings sidebar resizes with mouse and keyboard and remembers its width", async ({ workbench: page }) => {
await page.keyboard.press("Meta+,");
const dialog = page.getByRole("dialog", { name: "Settings", exact: true });
const handle = dialog.getByRole("separator", { name: "Settings pages", exact: true });
await expect(handle).toBeVisible();
const sidebar = dialog.locator("#navigation");
const initial = (await sidebar.boundingBox())!.width;
const box = (await handle.boundingBox())!;
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
await page.mouse.down();
await page.mouse.move(box.x + 60, box.y + box.height / 2);
await page.mouse.up();
await expect.poll(async () => (await sidebar.boundingBox())!.width).toBeGreaterThan(initial + 40);
const resized = (await sidebar.boundingBox())!.width;
await dialog.getByRole("button", { name: "Cancel", exact: true }).click();
await page.keyboard.press("Meta+,");
await expect(handle).toBeVisible();
await expect.poll(async () => (await sidebar.boundingBox())!.width).toBeCloseTo(resized, 0);
await handle.focus();
await page.keyboard.press("ArrowLeft");
await expect.poll(async () => (await sidebar.boundingBox())!.width).toBeLessThan(resized);
});