test(ui): isolate mobile form control browser fixtures

This commit is contained in:
Vincent Koc
2026-06-19 15:59:33 +02:00
parent b677ea6726
commit f062171c54

View File

@@ -1,6 +1,6 @@
// Control UI tests cover form controls behavior.
import { chromium, type Browser, type Page } from "playwright";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { describe, expect, it } from "vitest";
import { readStyleSheet } from "../../../test/helpers/ui-style-fixtures.js";
import {
canRunPlaywrightChromium,
@@ -12,7 +12,10 @@ const describeBrowserLayout = canRunPlaywrightChromium(chromiumExecutablePath)
? describe
: describe.skip;
let browser: Browser;
type MobileFixture = {
browser: Browser;
page: Page;
};
function readUiCss(): string {
const files = [
@@ -53,29 +56,35 @@ function controlsHtml() {
`;
}
async function openMobileFixture(): Promise<Page> {
const page = await browser.newPage({
hasTouch: true,
isMobile: true,
viewport: { width: 390, height: 844 },
});
await page.setContent(
`<!doctype html><html data-theme-mode="light"><head><style>${readUiCss()}</style></head><body>${controlsHtml()}</body></html>`,
);
return page;
async function openMobileFixture(): Promise<MobileFixture> {
const browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true });
let page: Page | undefined;
try {
page = await browser.newPage({
hasTouch: true,
isMobile: true,
viewport: { width: 390, height: 844 },
});
await page.setContent(
`<!doctype html><html data-theme-mode="light"><head><style>${readUiCss()}</style></head><body>${controlsHtml()}</body></html>`,
);
return { browser, page };
} catch (error) {
await page?.close().catch(() => {});
await browser.close().catch(() => {});
throw error;
}
}
async function closeMobileFixture(fixture: MobileFixture): Promise<void> {
await fixture.page.close().catch(() => {});
await fixture.browser.close().catch(() => {});
}
describeBrowserLayout("touch-primary form controls", () => {
beforeAll(async () => {
browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true });
});
afterAll(async () => {
await browser.close();
});
it("keeps text-entry controls large enough to avoid mobile focus zoom", async () => {
const page = await openMobileFixture();
const fixture = await openMobileFixture();
const { page } = fixture;
try {
const metrics = await page.evaluate(() => {
const selectors = [
@@ -119,12 +128,13 @@ describeBrowserLayout("touch-primary form controls", () => {
expect(size.fontSize, size.selector).toBeGreaterThanOrEqual(16);
}
} finally {
await page.close();
await closeMobileFixture(fixture);
}
});
it("keeps native select affordances visible in light mode", async () => {
const page = await openMobileFixture();
const fixture = await openMobileFixture();
const { page } = fixture;
try {
const selects = await page.locator(".cfg-select, .field select").evaluateAll((nodes) =>
nodes.map((node) => {
@@ -144,7 +154,7 @@ describeBrowserLayout("touch-primary form controls", () => {
expect(select.repeat).toContain("no-repeat");
}
} finally {
await page.close();
await closeMobileFixture(fixture);
}
});
});