
























11// Control UI tests cover sessions 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,
@@ -19,7 +19,10 @@ const describeBrowserLayout = canRunPlaywrightChromium(chromiumExecutablePath)
1919 ? describe
2020 : describe.skip;
212122-let browser: Browser;
22+type BrowserFixture = {
23+browser: Browser;
24+page: Page;
25+};
23262427function readUiCss(): string {
2528const files = [
@@ -156,82 +159,91 @@ function sessionsTableHtml() {
156159 `;
157160}
158161159-async function openFixture(width: number, height: number): Promise<Page> {
160-const page = await browser.newPage({ viewport: { width, height } });
161-await page.setContent(
162-`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${sessionsTableHtml()}</body></html>`,
163-);
164-return page;
162+async function openFixture(width: number, height: number): Promise<BrowserFixture> {
163+const browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true });
164+let page: Page | undefined;
165+try {
166+page = await browser.newPage({ viewport: { width, height } });
167+await page.setContent(
168+`<!doctype html><html><head><style>${readUiCss()}</style></head><body>${sessionsTableHtml()}</body></html>`,
169+);
170+return { browser, page };
171+} catch (error) {
172+await page?.close().catch(() => {});
173+await browser.close().catch(() => {});
174+throw error;
175+}
165176}
166177167-describeBrowserLayout("sessions responsive browser layout", () => {
168-beforeAll(async () => {
169-browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true });
170-});
171-172-afterAll(async () => {
173-await browser.close();
174-});
178+async function closeFixture(fixture: BrowserFixture): Promise<void> {
179+await fixture.page.close().catch(() => {});
180+await fixture.browser.close().catch(() => {});
181+}
175182183+describeBrowserLayout("sessions responsive browser layout", () => {
176184it.each(VIEWPORTS)("keeps compaction details visible at %dx%d", async (width, height) => {
177-const page = await openFixture(width, height);
178-const metrics = await page.evaluate(() => {
179-const container = document.querySelector(".data-table-container");
180-const compaction = document.querySelector(".session-compaction-cell");
181-const trigger = document.querySelector(".session-compaction-trigger");
182-const status = document.querySelector(".session-status-badge");
183-const statusLabel = document.querySelector(".session-status-badge__label");
184-const runtime = document.querySelector(".session-runtime-cell .mono");
185-const kind = document.querySelector(".data-table-badge");
186-const key = document.querySelector(".session-key-cell .session-link");
187-const details = document.querySelector(".session-details-panel");
188-if (
189-!(container instanceof HTMLElement) ||
190-!(compaction instanceof HTMLElement) ||
191-!(status instanceof HTMLElement) ||
192-!(statusLabel instanceof HTMLElement) ||
193-!(runtime instanceof HTMLElement) ||
194-!(kind instanceof HTMLElement) ||
195-!(key instanceof HTMLElement)
196-) {
197-throw new Error("Missing sessions table fixture elements");
198-}
199-const containerRect = container.getBoundingClientRect();
200-const compactionRect = compaction.getBoundingClientRect();
201-const statusRect = status.getBoundingClientRect();
202-return {
203-bodyOverflow: document.documentElement.scrollWidth - window.innerWidth,
204-compactionText: compaction.textContent?.trim(),
205-statusText: status.textContent?.trim(),
206-runtimeText: runtime.textContent?.trim(),
207-keyWhiteSpace: getComputedStyle(key).whiteSpace,
208-kindWhiteSpace: getComputedStyle(kind).whiteSpace,
209-statusWhiteSpace: getComputedStyle(status).whiteSpace,
210-runtimeWhiteSpace: getComputedStyle(runtime).whiteSpace,
211-hasTrigger: trigger !== null,
212-hasLegacyButton: document.querySelector(".session-checkpoint-toggle") !== null,
213-hasDetails: details !== null,
214-compactionVisible:
215-compactionRect.left >= containerRect.left && compactionRect.right <= containerRect.right,
216-statusVisible:
217-statusRect.left >= containerRect.left && statusRect.right <= containerRect.right,
218-};
219-});
220-221-expect(metrics.bodyOverflow).toBeLessThanOrEqual(1);
222-expect(metrics.compactionText).toBe("1 Checkpoint");
223-expect(metrics.statusText).toBe("Live");
224-expect(metrics.runtimeText).toBe("claude-cli (fallback none)");
225-expect(metrics.keyWhiteSpace).toBe("nowrap");
226-expect(metrics.kindWhiteSpace).toBe("nowrap");
227-expect(metrics.statusWhiteSpace).toBe("nowrap");
228-expect(metrics.runtimeWhiteSpace).toBe("nowrap");
229-expect(metrics.hasTrigger).toBe(true);
230-expect(metrics.hasLegacyButton).toBe(false);
231-expect(metrics.hasDetails).toBe(true);
232-expect(metrics.compactionVisible).toBe(true);
233-expect(metrics.statusVisible).toBe(true);
185+const fixture = await openFixture(width, height);
186+const { page } = fixture;
187+try {
188+const metrics = await page.evaluate(() => {
189+const container = document.querySelector(".data-table-container");
190+const compaction = document.querySelector(".session-compaction-cell");
191+const trigger = document.querySelector(".session-compaction-trigger");
192+const status = document.querySelector(".session-status-badge");
193+const statusLabel = document.querySelector(".session-status-badge__label");
194+const runtime = document.querySelector(".session-runtime-cell .mono");
195+const kind = document.querySelector(".data-table-badge");
196+const key = document.querySelector(".session-key-cell .session-link");
197+const details = document.querySelector(".session-details-panel");
198+if (
199+!(container instanceof HTMLElement) ||
200+!(compaction instanceof HTMLElement) ||
201+!(status instanceof HTMLElement) ||
202+!(statusLabel instanceof HTMLElement) ||
203+!(runtime instanceof HTMLElement) ||
204+!(kind instanceof HTMLElement) ||
205+!(key instanceof HTMLElement)
206+) {
207+throw new Error("Missing sessions table fixture elements");
208+}
209+const containerRect = container.getBoundingClientRect();
210+const compactionRect = compaction.getBoundingClientRect();
211+const statusRect = status.getBoundingClientRect();
212+return {
213+bodyOverflow: document.documentElement.scrollWidth - window.innerWidth,
214+compactionText: compaction.textContent?.trim(),
215+statusText: status.textContent?.trim(),
216+runtimeText: runtime.textContent?.trim(),
217+keyWhiteSpace: getComputedStyle(key).whiteSpace,
218+kindWhiteSpace: getComputedStyle(kind).whiteSpace,
219+statusWhiteSpace: getComputedStyle(status).whiteSpace,
220+runtimeWhiteSpace: getComputedStyle(runtime).whiteSpace,
221+hasTrigger: trigger !== null,
222+hasLegacyButton: document.querySelector(".session-checkpoint-toggle") !== null,
223+hasDetails: details !== null,
224+compactionVisible:
225+compactionRect.left >= containerRect.left &&
226+compactionRect.right <= containerRect.right,
227+statusVisible:
228+statusRect.left >= containerRect.left && statusRect.right <= containerRect.right,
229+};
230+});
234231235-await page.close();
232+expect(metrics.bodyOverflow).toBeLessThanOrEqual(1);
233+expect(metrics.compactionText).toBe("1 Checkpoint");
234+expect(metrics.statusText).toBe("Live");
235+expect(metrics.runtimeText).toBe("claude-cli (fallback none)");
236+expect(metrics.keyWhiteSpace).toBe("nowrap");
237+expect(metrics.kindWhiteSpace).toBe("nowrap");
238+expect(metrics.statusWhiteSpace).toBe("nowrap");
239+expect(metrics.runtimeWhiteSpace).toBe("nowrap");
240+expect(metrics.hasTrigger).toBe(true);
241+expect(metrics.hasLegacyButton).toBe(false);
242+expect(metrics.hasDetails).toBe(true);
243+expect(metrics.compactionVisible).toBe(true);
244+expect(metrics.statusVisible).toBe(true);
245+} finally {
246+await closeFixture(fixture);
247+}
236248});
237249});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。