

























11// Control UI tests cover chat responsive behavior.
22import { chromium, type Browser, type Page } from "playwright";
3-import { afterAll, beforeAll, describe, expect, it } from "vitest";
3+import { describe, expect, it } from "vitest";
44import { readStyleSheet } from "../../../../test/helpers/ui-style-fixtures.js";
55import {
66canRunPlaywrightChromium,
@@ -22,7 +22,7 @@ const describeBrowserLayout = canRunPlaywrightChromium(chromiumExecutablePath)
2222 ? describe
2323 : describe.skip;
242425-let browser: Browser;
25+const pageBrowsers = new WeakMap<Page, Browser>();
26262727type ControlRect = {
2828x: number;
@@ -262,11 +262,37 @@ async function openFixture(
262262height: number,
263263opts: { sideResult?: boolean; singleAgent?: boolean } = {},
264264) {
265-const page = await browser.newPage({ viewport: { width, height } });
266-await page.setContent(
267-`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${chatHtml(opts)}</body></html>`,
268-);
269-return page;
265+const page = await openBrowserPage(width, height);
266+try {
267+await page.setContent(
268+`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${chatHtml(opts)}</body></html>`,
269+);
270+return page;
271+} catch (error) {
272+await closeBrowserPage(page);
273+throw error;
274+}
275+}
276+277+async function openBrowserPage(width: number, height: number): Promise<Page> {
278+const browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true });
279+let page: Page | undefined;
280+try {
281+page = await browser.newPage({ viewport: { width, height } });
282+pageBrowsers.set(page, browser);
283+return page;
284+} catch (error) {
285+await page?.close().catch(() => {});
286+await browser.close().catch(() => {});
287+throw error;
288+}
289+}
290+291+async function closeBrowserPage(page: Page): Promise<void> {
292+const browser = pageBrowsers.get(page);
293+pageBrowsers.delete(page);
294+await page.close().catch(() => {});
295+await browser?.close().catch(() => {});
270296}
271297272298async function getRect(page: Page, selector: string) {
@@ -317,11 +343,16 @@ function rectsOverlap(
317343}
318344319345async function openHeaderFixture(width: number, height: number, opts: { hidden?: boolean } = {}) {
320-const page = await browser.newPage({ viewport: { width, height } });
321-await page.setContent(
322-`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${chatHeaderControlsHtml(Boolean(opts.hidden))}</body></html>`,
323-);
324-return page;
346+const page = await openBrowserPage(width, height);
347+try {
348+await page.setContent(
349+`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${chatHeaderControlsHtml(Boolean(opts.hidden))}</body></html>`,
350+);
351+return page;
352+} catch (error) {
353+await closeBrowserPage(page);
354+throw error;
355+}
325356}
326357327358async function expectNoHorizontalOverflow(page: Page) {
@@ -334,14 +365,6 @@ async function expectNoHorizontalOverflow(page: Page) {
334365expect(metrics.body).toBeLessThanOrEqual(metrics.viewport + 1);
335366}
336367337-beforeAll(async () => {
338-browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true });
339-});
340-341-afterAll(async () => {
342-await browser.close();
343-});
344-345368describeBrowserLayout("chat responsive browser layout", () => {
346369it.each([
347370[1120, 740],
@@ -378,7 +401,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
378401expect(session.width / agent.width).toBeGreaterThan(1.25);
379402expect(session.width / agent.width).toBeLessThan(1.55);
380403} finally {
381-await page.close();
404+await closeBrowserPage(page);
382405}
383406});
384407@@ -399,7 +422,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
399422expect(hiddenState.opacity).toBe("0");
400423expect(hiddenState.pointerEvents).toBe("none");
401424} finally {
402-await page.close();
425+await closeBrowserPage(page);
403426}
404427});
405428@@ -410,7 +433,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
410433const code = await getBoundingBox(page, ".chat-text pre");
411434expect(code.x + code.width).toBeLessThanOrEqual(width + 1);
412435} finally {
413-await page.close();
436+await closeBrowserPage(page);
414437}
415438});
416439@@ -420,7 +443,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
420443] as const)(
421444"keeps short assistant text clear of bubble actions at %sx%s",
422445async (width, height) => {
423-const page = await browser.newPage({ viewport: { width, height } });
446+const page = await openBrowserPage(width, height);
424447try {
425448await page.setContent(
426449`<!doctype html><html><head><style>${readUiCss()}</style></head><body>
@@ -445,7 +468,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
445468const actions = await getRect(page, ".chat-bubble-actions");
446469expect(text.right).toBeLessThanOrEqual(actions.left - 1);
447470} finally {
448-await page.close();
471+await closeBrowserPage(page);
449472}
450473},
451474);
@@ -454,7 +477,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
454477[320, 568],
455478[1366, 900],
456479] as const)("wraps long inline code without clipping at %sx%s", async (width, height) => {
457-const page = await browser.newPage({ viewport: { width, height } });
480+const page = await openBrowserPage(width, height);
458481try {
459482await page.setContent(
460483`<!doctype html><html><head><style>${readUiCss()}</style></head><body>
@@ -480,7 +503,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
480503const inlineCode = await getRect(page, ".chat-text p code");
481504expect(inlineCode.right).toBeLessThanOrEqual(bubble.right + 1);
482505} finally {
483-await page.close();
506+await closeBrowserPage(page);
484507}
485508});
486509@@ -546,7 +569,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
546569expect(size.height).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
547570}
548571} finally {
549-await page.close();
572+await closeBrowserPage(page);
550573}
551574},
552575);
@@ -568,7 +591,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
568591expect(size.height).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
569592}
570593} finally {
571-await page.close();
594+await closeBrowserPage(page);
572595}
573596});
574597@@ -625,7 +648,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
625648expect(settings.height).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
626649expect(settingsLabel.display).toBe("none");
627650} finally {
628-await page.close();
651+await closeBrowserPage(page);
629652}
630653},
631654);
@@ -640,7 +663,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
640663expect(model.y).toBeGreaterThan(session.y);
641664expect(model.width).toBe(session.width);
642665} finally {
643-await page.close();
666+await closeBrowserPage(page);
644667}
645668});
646669@@ -653,7 +676,7 @@ describeBrowserLayout("chat responsive browser layout", () => {
653676.evaluate((node) => getComputedStyle(node).position);
654677expect(position).toBe("fixed");
655678} finally {
656-await page.close();
679+await closeBrowserPage(page);
657680}
658681});
659682});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。